home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 722 / 722.xpi / chrome / noscript.jar / content / noscript / Main.js < prev    next >
Text File  |  2010-02-12  |  156KB  |  4,463 lines

  1. const WP_STATE_START = CI.nsIWebProgressListener.STATE_START;
  2. const WP_STATE_STOP = CI.nsIWebProgressListener.STATE_STOP;
  3. const WP_STATE_DOC = CI.nsIWebProgressListener.STATE_IS_DOCUMENT;
  4. const WP_STATE_START_DOC = WP_STATE_START | WP_STATE_DOC;
  5. const WP_STATE_RESTORING = CI.nsIWebProgressListener.STATE_RESTORING;
  6.  
  7. const LF_VALIDATE_ALWAYS = CI.nsIRequest.VALIDATE_ALWAYS;
  8. const LF_LOAD_BYPASS_ALL_CACHES = CI.nsIRequest.LOAD_BYPASS_CACHE | CI.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE;
  9.  
  10. const NS_OK = 0;
  11. const NS_BINDING_ABORTED = 0x804b0002;
  12. const NS_BINDING_REDIRECTED = 0x804b0003;
  13. const NS_ERROR_UNKNOWN_HOST = 0x804b001e;
  14. const NS_ERROR_REDIRECT_LOOP = 0x804b001f;
  15. const NS_ERROR_CONNECTION_REFUSED = 0x804b000e;
  16. const NS_ERROR_NOT_AVAILABLE = 0x804b0111;
  17.  
  18. const LOG_CONTENT_BLOCK = 1;
  19. const LOG_CONTENT_CALL = 2;
  20. const LOG_CONTENT_INTERCEPT = 4;
  21. const LOG_CHROME_WIN = 8;
  22. const LOG_XSS_FILTER = 16;
  23. const LOG_INJECTION_CHECK = 32;
  24. const LOG_DOM = 64;
  25. const LOG_JS = 128;
  26. const LOG_LEAKS = 1024;
  27. const LOG_SNIFF = 2048;
  28. const LOG_CLEARCLICK = 4096;
  29. const LOG_ABE = 8192;
  30.  
  31. const HTML_NS = "http://www.w3.org/1999/xhtml";
  32.  
  33. const WHERE_UNTRUSTED = 1;
  34. const WHERE_TRUSTED = 2;
  35. const ANYWHERE = 3;
  36.  
  37. const DUMMYOBJ = {};
  38.  
  39. INCLUDE('Sites', 'AddressMatcher', 'DOM', 'IOUtil', 'Policy', 'RequestWatchdog', 'HTTPS', 'ClearClickHandler', 'URIValidator', 'ScriptSurrogate', 'ABE');
  40.  
  41. var ns = singleton = {
  42.   VERSION: VERSION
  43. ,
  44.   QueryInterface: xpcom_generateQI(SERVICE_IIDS),
  45.   generateQI: xpcom_generateQI
  46. ,
  47.   // nsIObserver implementation 
  48.   observe: function(subject, topic, data) {
  49.  
  50.     if (subject instanceof CI.nsIPrefBranch2) {
  51.       this.syncPrefs(subject, data);
  52.     } else {
  53.       switch (topic) {
  54.         case "xpcom-shutdown":
  55.           this.unregister();
  56.           break;
  57.         
  58.         case "profile-before-change": 
  59.           this.dispose();
  60.           Thread.hostRunning = false;
  61.           break;
  62.         case "profile-after-change":
  63.           Thread.hostRunning = true;
  64.           try {
  65.             this.init();
  66.           } catch(e) {
  67.             this.dump("Init error -- " + e.message);
  68.           }
  69.           break;
  70.         
  71.         case "em-action-requested":
  72.           if ((subject instanceof CI.nsIUpdateItem)
  73.               && subject.id == EXTENSION_ID ) {
  74.             if (data == "item-uninstalled" || data == "item-disabled") {
  75.               this.uninstalling = true;
  76.             } else if (data == "item-enabled") {
  77.               this.uninstalling = false;
  78.             }
  79.           }
  80.         break;
  81.         
  82.         case "toplevel-window-ready":
  83.           this.registerToplevel(subject); 
  84.         break;
  85.       
  86.         case "private-browsing":
  87.           if (data == "enter") {
  88.             STS.enterPrivateBrowsing();
  89.           }
  90.           if (data == "exit") {
  91.             this.eraseTemp();
  92.             STS.exitPrivateBrowsing();
  93.           }
  94.         // break; 
  95.         case "browser:purge-session-history":
  96.           this.recentlyBlocked = [];
  97.           STS.eraseDB();
  98.         break;
  99.       }
  100.     }
  101.   },
  102.   
  103.   registerToplevel: function(window) {
  104.     
  105.     if ((window instanceof CI.nsIDOMChromeWindow) && !window.opener &&
  106.        (window instanceof CI.nsIDOMNSEventTarget)) {
  107.       window.isNewToplevel = true;
  108.       if (this.consoleDump & LOG_CHROME_WIN) {
  109.         this.dump("Toplevel register, true");
  110.       }
  111.       this.handleToplevel.ns = this;
  112.       window.addEventListener("load", this.handleToplevel, false);
  113.     }
  114.   },
  115.   handleToplevel: function(ev) {
  116.     // this resets newtoplevel status to true after chrome
  117.     const window = ev.currentTarget;
  118.     const callee = arguments.callee;
  119.     switch (ev.type) {
  120.       case "load":
  121.         window.removeEventListener("load", callee, false);
  122.         window.addEventListener("unload", callee, false);
  123.         ns.delayExec(callee, 0, { type: "timeout", currentTarget: window });
  124.         break;
  125.       case "timeout":
  126.       case "unload":
  127.         window.isNewToplevel = false;
  128.         window.removeEventListener("unload", callee, false);
  129.     }
  130.     if (ns.consoleDump & LOG_CHROME_WIN) 
  131.       ns.dump("Toplevel " + ev.type + ", " + window.isNewToplevel);
  132.     
  133.   },
  134.   
  135.   OBSERVED_TOPICS: ["profile-before-change", "xpcom-shutdown", "profile-after-change", "profile-after-change", "toplevel-window-ready",
  136.                     "browser:purge-session-history", "private-browsing"],
  137.   register: function() {
  138.     this.OBSERVED_TOPICS.forEach(function(topic) {
  139.       OS.addObserver(this, topic, true);
  140.     }, this);
  141.   },
  142.   unregister: function() {
  143.     this.OBSERVED_TOPICS.forEach(function(topic) {
  144.       OS.removeObserver(this, topic);
  145.     }, this);
  146.   }
  147. ,
  148.   
  149.   // Preference driven properties
  150.   autoAllow: false,
  151.  
  152.   blockNSWB: false,
  153.   
  154.   consoleDump: 0,
  155.   consoleLog: false,
  156.   
  157.   truncateTitle: true,
  158.   truncateTitleLen: 255,
  159.   
  160.   showPlaceholder: true,
  161.   showUntrustedPlaceholder: true,
  162.   collapseObject: false,
  163.   abpRemoveTabs: false,
  164.   opaqueObject: 1,
  165.   clearClick: 3,
  166.  
  167.   
  168.   forbidSomeContent: true,
  169.   contentBlocker: false,
  170.   
  171.   forbidChromeScripts: false,
  172.   forbidData: true,
  173.   
  174.   forbidJarDocuments: true,
  175.   forbidJarDocumentsExceptions: null,
  176.   
  177.   forbidJava: true,
  178.   forbidFlash: false,
  179.   forbidFlash: true,
  180.   forbidPlugins: true,
  181.   forbidMedia: true,
  182.   forbidFonts: true,
  183.   forbidIFrames: false, 
  184.   forbidIFramesContext: 2, // 0 = all iframes, 1 = different site, 2 = different domain, 3 = different base domain
  185.   forbidFrames: false,
  186.   
  187.   alwaysBlockUntrustedContent: true,
  188.   docShellJSBlocking: 1, // 0 - don't touch docShells, 1 - block untrusted, 2 - block not whitelisted
  189.   
  190.   forbidXBL: 4,
  191.   forbidXHR: 2,
  192.   injectionCheck: 2,
  193.   injectionCheckSubframes: true,
  194.   
  195.   jsredirectIgnore: false,
  196.   jsredirectFollow: false,
  197.   jsredirectForceShow: false,
  198.   emulateFrameBreak: true,
  199.   
  200.   jsHack: null,
  201.   jsHackRegExp: null,
  202.   
  203.   flashPatch: true,
  204.   silverlightPatch: true,
  205.   
  206.   nselNever: false,
  207.   nselForce: true,
  208.  
  209.   filterXGetRx: "(?:<+(?=[^<>=\\d\\. ])|[\\\\'\"\\x00-\\x07\\x09\\x0B\\x0C\\x0E-\\x1F\\x7F])",
  210.   filterXGetUserRx: "",
  211.   
  212.   
  213.   whitelistRegExp: null,
  214.   allowedMimeRegExp: null, 
  215.   hideOnUnloadRegExp: null,
  216.   requireReloadRegExp: null,
  217.   ignorePorts: true,
  218.   
  219.   inclusionTypeChecking: true,
  220.   
  221.   resetDefaultPrefs: function(prefs, exclude) {
  222.     exclude = exclude || [];
  223.     var children = prefs.getChildList("", {});
  224.     for (var j = children.length; j-- > 0;) {
  225.       if (exclude.indexOf(children[j]) < 0) {
  226.         if (prefs.prefHasUserValue(children[j])) {
  227.           dump("Resetting noscript." + children[j] + "\n");
  228.           try {
  229.             prefs.clearUserPref(children[j]);
  230.           } catch(e) { dump(e + "\n") }
  231.         }
  232.       }
  233.     }
  234.     this.savePrefs();
  235.   },
  236.   
  237.   resetDefaultGeneralPrefs: function() {
  238.     this.resetDefaultPrefs(this.prefs, ['version']);
  239.   },
  240.   
  241.   resetDefaultSitePrefs: function() {
  242.     this.eraseTemp();
  243.     this.setJSEnabled(this.splitList(this.getPref("default")), true, true);
  244.   },
  245.   
  246.   resetDefaults: function() {
  247.     this.resetDefaultGeneralPrefs();
  248.     this.jsEnabled = false;
  249.     this.resetDefaultSitePrefs();
  250.     ABE.resetDefaults();
  251.   },
  252.   
  253.   syncPrefs: function(branch, name) {
  254.     switch (name) {
  255.       case "sites":
  256.         if (this.jsPolicySites.settingPref) return;
  257.         if (this.locked) this.defaultCaps.lockPref(this.POLICY_NAME + ".sites");
  258.         if (!this.jsPolicySites.fromPref(this.policyPB)) {
  259.           this.resetDefaultSitePrefs();
  260.         }
  261.         break;
  262.       case "temp":
  263.         this.tempSites.fromPref(branch, name);
  264.       break;
  265.       case "gtemp":
  266.         this.gTempSites.fromPref(branch, name);
  267.       break;
  268.       case "untrusted":
  269.         this.untrustedSites.fromPref(branch, name);
  270.       break;
  271.       case "default.javascript.enabled":
  272.           if (dc.getCharPref(name) != "noAccess") {
  273.             dc.unlockPref(name);
  274.             dc.setCharPref(name, "noAccess");
  275.           }
  276.          dc.lockPref(name);
  277.          break;
  278.       case "enabled":
  279.         try {
  280.           this.mozJSEnabled = this.mozJSPref.getBoolPref("enabled");
  281.         } catch(ex) {
  282.           this.mozJSPref.setBoolPref("enabled", this.mozJSEnabled = true);
  283.         }
  284.       break;
  285.       case "forbidJava":
  286.       case "forbidFlash":
  287.       case "forbidSilverlight":
  288.       case "forbidPlugins":
  289.       case "forbidMedia":
  290.       case "forbidFonts":
  291.       case "forbidIFrames":
  292.       case "forbidFrames":
  293.         this[name]=this.getPref(name, this[name]);
  294.         this.forbidSomeContent = this.forbidJava || this.forbidFlash ||
  295.           this.forbidSilverlight || this.forbidPlugins ||
  296.           this.forbidMedia || this.forbidFonts ||
  297.           this.forbidIFrames || this.forbidFrames;
  298.       break;
  299.       
  300.       case "abp.removeTabs":
  301.         this.abpRemoveTabs = this.abpInstalled && this.getPref(name);
  302.       break;
  303.     
  304.       case "emulateFrameBreak":
  305.       case "filterXPost":
  306.       case "filterXGet":
  307.       case "safeToplevel":
  308.       case "autoAllow":
  309.       case "contentBlocker":
  310.       case "docShellJSBlocking":
  311.       case "showUntrustedPlaceholder":
  312.       case "collapseObject":
  313.       case "truncateTitle":
  314.       case "truncateTitleLen":
  315.       case "forbidChromeScripts":
  316.       case "forbidData":
  317.       case "forbidJarDocuments":
  318.       case "forbidMetaRefresh":
  319.       case "forbidIFramesContext":
  320.       case "forbidXBL":
  321.       case "forbidXHR":
  322.       case "ignorePorts":
  323.       case "injectionCheck":
  324.       case "jsredirectFollow":
  325.       case "jsredirectIgnore":
  326.       case "jsredirectForceShow":
  327.       case "jsHack":
  328.       case "consoleLog":
  329.       case "flashPatch":
  330.       case "silverlightPatch":
  331.       case "inclusionTypeChecking":
  332.         this[name] = this.getPref(name, this[name]);  
  333.       break;
  334.       
  335.       case "clearClick.exceptions":
  336.       case "clearClick.subexceptions":
  337.         ClearClickHandler.prototype[name.split('.')[1]] = AddressMatcher.create(this.getPref(name, ''));
  338.       break;
  339.       
  340.       case "secureCookies":
  341.       case "allowHttpsOnly":
  342.         HTTPS[name] = this.getPref(name, HTTPS[name]);  
  343.       break;
  344.       
  345.     
  346.     
  347.       case "secureCookiesExceptions":
  348.       case "secureCookiesForced":
  349.       case "httpsForced":
  350.       case "httpsForcedExceptions":
  351.         HTTPS[name] = AddressMatcher.create(this.getPref(name, ''));
  352.       break;
  353.       
  354.       case "proxiedDNS":
  355.       case "asyncNetworking":
  356.         IOUtil[name] = this.getPref(name, IOUtil[name]);
  357.       break;
  358.       
  359.       case "ABE.enabled":
  360.       case "ABE.siteEnabled":
  361.       case "ABE.allowRulesetRedir":
  362.       case "ABE.disabledRulesetNames":
  363.       case "ABE.legacySupport":
  364.       case "ABE.skipBrowserRequests":
  365.         ABE[name.substring(4)] = this.getPref(name);
  366.       break;
  367.       
  368.       case "STS.enabled":
  369.         STS[name.substring(4)] = this.getPref(name);
  370.       break;
  371.       
  372.       case "consoleDump":
  373.         this[name] = this.getPref(name, this[name]);
  374.         this.injectionChecker.logEnabled = this.consoleDump & LOG_INJECTION_CHECK;
  375.         DOM.consoleDump = this.consoleDump & LOG_DOM;
  376.         ABE.consoleDump = this.consoleDump & LOG_ABE;
  377.       break;
  378.       case "global":
  379.         this.globalJS = this.getPref(name, false);
  380.       break;
  381.       
  382.       case "alwaysBlockUntrustedContent":
  383.         this[name] = this.getPref(name, this[name]);
  384.         this.initContentPolicy();
  385.       break;
  386.       
  387.       case "forbidMetaRefreshRemember":
  388.         if (!this.getPref(name)) this.metaRefreshWhitelist = {};
  389.       break;
  390.       
  391.       // single rx
  392.       case "filterXGetRx":
  393.       case "filterXGetUserRx":
  394.         this.updateRxPref(name, this[name], "g");
  395.       break;
  396.       
  397.       // multiple rx
  398.       case "forbidJarDocumentsExceptions":
  399.       case "filterXExceptions":
  400.       case "jsHackRegExp":
  401.         this.updateRxPref(name, "", "", this.rxParsers.multi);
  402.       break;
  403.       
  404.       // multiple rx autoanchored
  405.       case "hideOnUnloadRegExp":
  406.         this.updateStyleSheet("." + this.hideObjClassName + " {display: none !important}", true);
  407.       case "allowedMimeRegExp":
  408.       case "requireReloadRegExp":
  409.       case "whitelistRegExp":
  410.         this.updateRxPref(name, "", "^", this.rxParsers.multi);
  411.       break;
  412.         
  413.         
  414.       case "safeJSRx":
  415.         this.initSafeJSRx();
  416.       break;
  417.       
  418.       case "allowClipboard":
  419.         this.updateExtraPerm(name, "Clipboard", ["cutcopy", "paste"]);
  420.       break;
  421.       case "allowLocalLinks":
  422.         this.updateExtraPerm(name, "checkloaduri", ["enabled"]);
  423.       break;
  424.       
  425.       case "blockNSWB":
  426.       case "nselForce":
  427.       case "nselNever":
  428.       case "showPlaceholder":
  429.       case "opacizeObject":
  430.       case "clearClick":
  431.         this.updateCssPref(name);
  432.         if ((name == "nselNever") && this.getPref("nselNever") && !this.blockNSWB) {
  433.           this.setPref("blockNSWB", true);
  434.         }
  435.       break;
  436.       
  437.       case "policynames":
  438.         this.setupJSCaps();
  439.       break;
  440.     }
  441.   },
  442.   
  443.   rxParsers: {
  444.     simple: function(s, flags) {
  445.       var anchor = /\^/.test(flags);
  446.       return new RegExp(anchor ? ns.rxParsers.anchor(s) : s,
  447.         anchor ? flags.replace(/\^/g, '') : flags);
  448.     },
  449.     anchor: function(s) {
  450.       return !/^\^|\$$/.test(s) ? s : "^" + s + "$";
  451.     },
  452.     multi: function(s, flags) {
  453.       var anchor = /\^/.test(flags);
  454.       var lines = s.split(/[\n\r]+/)
  455.           .filter(function(l) { return /\S/.test(l) });
  456.       return new RegExp(
  457.         "(?:" +
  458.         (anchor ? lines.map(ns.rxParsers.anchor) : lines).join('|')
  459.         + ")",
  460.         anchor ? flags.replace(/\^/g, '') : flags);
  461.     }
  462.   },
  463.   updateRxPref: function(name, def, flags, parseRx) {
  464.     parseRx = parseRx || this.rxParsers.simple;
  465.     var s = this.getPref(name, def);
  466.     if (!s) {
  467.       this[name] = null;
  468.     } else
  469.     {
  470.       
  471.       try {
  472.         this[name] = parseRx(this.getPref(name, def), flags);
  473.       } catch(e) {
  474.         if(this.consoleDump) this.dump("Error parsing regular expression " + name + ", " + e);
  475.         this[name] = parseRx(def, flags);
  476.       }
  477.     }
  478.   },
  479.   
  480.   
  481.   updateExtraPerm: function(prefName, baseName, names) {
  482.     var cpName;
  483.     var enabled = this.getPref(prefName, false);
  484.     for (var j = names.length; j-- > 0;) {
  485.       cpName = this.POLICY_NAME + "." + baseName + "." + names[j];
  486.       try {
  487.         if (enabled) {
  488.           this.caps.setCharPref(cpName, "allAccess");
  489.         } else {
  490.           if (this.caps.prefHasUserValue(cpName)) {
  491.             this.caps.clearUserPref(cpName);
  492.           }
  493.         }
  494.       } catch(ex) {}
  495.     }
  496.   },
  497.   
  498.   updateCssPref: function(name) {
  499.     var value = this[name] = this.getPref(name);
  500.     var sheet;
  501.     switch(name) {
  502.       case "nselForce":
  503.         sheet = "noscript.noscript-show, span.noscript-show { display: inline !important } span.noscript-show { padding: 0px; margin: 0px; border: none; background: inherit; color: inherit }";
  504.         break;
  505.       case "nselNever":
  506.         sheet = "noscript, noscript * { display: none !important }";
  507.         break;
  508.       case "blockNSWB": 
  509.         sheet = "noscript, noscript * { background-image: none !important; list-style-image: none !important }";
  510.         break;
  511.       case "showPlaceholder": 
  512.         sheet = '.__noscriptPlaceholder__ > .__noscriptPlaceholder__1 { display: inline-block !important; ' +
  513.                 'outline-color: #fc0 !important; outline-style: solid !important; outline-width: 1px !important; outline-offset: -1px !important;' +
  514.                 'cursor: pointer !important; background: #ffffe0 url("' + 
  515.                     this.pluginPlaceholder + '") no-repeat left top !important; opacity: 0.6 !important; margin-top: 0px !important; margin-bottom: 0px !important;} ' +
  516.                 '.__noscriptPlaceholder__1 > .__noscriptPlaceholder__2 { display: inline-block !important; background-repeat: no-repeat !important; background-color: transparent !important; width: 100%; height: 100%; display: block; margin: 0px; border: none } ' +
  517.                 'noscript .__noscriptPlaceholder__ { display: inline !important; }';
  518.         if (this.geckoVersionCheck("1.9") < 0) {
  519.           sheet = sheet.replace(/ outline-/g, ' -moz-outline-').replace(/ inline-/g, '');
  520.         }
  521.       break;
  522.       case "clearClick":
  523.       case "opaqueObject":
  524.         sheet = ".__noscriptOpaqued__ { opacity: 1 !important; visibility: visible; filter: none !important } " +
  525.                 "iframe.__noscriptOpaqued__ { display: block !important; } " +
  526.                 "object.__noscriptOpaqued__, embed.__noscriptOpaqued__ { display: inline !important } " +
  527.                 ".__noscriptJustOpaqued__ { opacity: 1 !important; filter: none !important } " +
  528.                 ".__noscriptScrolling__ { overflow: auto !important; min-width: 52px !important; min-height: 52px !important } " +
  529.                 ".__noscriptNoScrolling__ { overflow: hidden !important } " +
  530.                 ".__noscriptHidden__ { visibility: hidden !important } " +
  531.                 ".__noscriptBlank__ { background-color: white !important; color: white !important; border-color: white !important; background-image: none !important }";
  532.                 
  533.       break;
  534.       default:
  535.         return;
  536.     };
  537.     this.updateStyleSheet(sheet, value);
  538.   },
  539.   
  540.   get sss() {
  541.     delete this.sss;
  542.     try {
  543.       return this.sss = CC["@mozilla.org/content/style-sheet-service;1"]
  544.                         .getService(CI.nsIStyleSheetService);
  545.     } catch(e) {
  546.       return this.sss = null;
  547.     }
  548.   },
  549.   
  550.   updateStyleSheet: function(sheet, enabled) {
  551.     const sss = this.sss;
  552.     if (!sss) return;
  553.     const uri = IOS.newURI("data:text/css;charset=utf8," + sheet, null, null);
  554.     if (sss.sheetRegistered(uri, sss.USER_SHEET)) {
  555.       if (!enabled) sss.unregisterSheet(uri, sss.USER_SHEET);
  556.     } else {
  557.       try {
  558.         if (enabled) sss.loadAndRegisterSheet(uri, sss.USER_SHEET);
  559.       } catch(e) {
  560.         this.log("[NoScript CSS] Can't register " + uri + ", " + e); 
  561.       }
  562.     }
  563.   },
  564.   
  565.   get getString() {
  566.     delete this.getString;
  567.     INCLUDE('Strings');
  568.     const ss = new Strings("noscript");
  569.     return this.getString = function(name, parms) { return ss.getString(name, parms) };
  570.   },
  571.   
  572.   _uninstalling: false,
  573.   get uninstalling() {
  574.     return this._uninstalling;
  575.   },
  576.   set uninstalling(b) {
  577.     if (!this._uninstalling) {
  578.       if (b) this.uninstallJob();
  579.     } else {
  580.       if (!b) this.undoUninstallJob();
  581.     }
  582.     return this._uninstalling = b;
  583.   }
  584. ,
  585.   _inited: false,
  586.   POLICY_NAME: "maonoscript",
  587.   prefService: null,
  588.   caps: null,
  589.   defaultCaps: null,
  590.   policyPB: null,
  591.   prefs: null,
  592.   mozJSPref: null,
  593.   mozJSEnabled: true,
  594.   disabled: false
  595. ,
  596.   // random resource aliases
  597.   contentBase: null,
  598.   skinBase: null,
  599.   hideObjClassName: "__noscriptHideObj__",
  600.   get hideObjClassNameRx() {
  601.     const v = new RegExp("\\b" + this.hideObjClassName + "\\s*", "g");
  602.     this.__defineGetter__("hideObjClassNameRx", function() { return v; });
  603.     return v;
  604.   },
  605.   pluginPlaceholder: "",
  606.   
  607.   _initResources: function() {
  608.     const ios = IOS;
  609.     var resProt = ios.getProtocolHandler("resource").QueryInterface(CI.nsIResProtocolHandler);
  610.     var base;
  611.     for each(var r in ["skin", "content"]) {
  612.       base = "noscript_" + Math.random();
  613.       resProt.setSubstitution(base, ios.newURI("chrome:noscript/" + r + "/", null, null));
  614.       this[r + "Base"] = "resource://" + base + "/";
  615.     }
  616.     this.pluginPlaceholder = this.skinBase + "icon32.png";
  617.   },
  618.   
  619.   init: function() {
  620.     if (this._inited) return false;
  621.     
  622.     this._inited = true;
  623.     
  624.     if (this.smUninstaller) this.smUninstaller.check();
  625.     
  626.     this._initResources();
  627.  
  628.     if (!this.requestWatchdog) {
  629.       this.requestWatchdog = new RequestWatchdog()
  630.     }
  631.     
  632.     OS.addObserver(this, "em-action-requested", true);
  633.     
  634.     const dls = CC['@mozilla.org/docloaderservice;1'].getService(CI.nsIWebProgress);
  635.     dls.addProgressListener(this, CI.nsIWebProgress.NOTIFY_LOCATION | CI.nsIWebProgress.NOTIFY_STATE_REQUEST | CI.nsIWebProgress.NOTIFY_STATUS);
  636.  
  637.  
  638.     const prefserv = this.prefService = CC["@mozilla.org/preferences-service;1"]
  639.       .getService(CI.nsIPrefService).QueryInterface(CI.nsIPrefBranch);
  640.  
  641.     const PBI = CI.nsIPrefBranch2;
  642.     this.caps = prefserv.getBranch("capability.policy.").QueryInterface(PBI);
  643.     this.defaultCaps = prefserv.getDefaultBranch(this.caps.root);
  644.  
  645.     this.policyPB = prefserv.getBranch("capability.policy." + this.POLICY_NAME + ".").QueryInterface(PBI);
  646.     this.prefs = prefserv.getBranch("noscript.").QueryInterface(PBI);
  647.     
  648.     this.policyPB.addObserver("sites", this, true);
  649.     
  650.     this.prefs.addObserver("", this, true);
  651.     this.mozJSPref = prefserv.getBranch("javascript.").QueryInterface(PBI);
  652.     this.mozJSPref.addObserver("enabled", this, true);
  653.     
  654.     this.mandatorySites.sitesString = this.getPref("mandatory", "chrome: about: resource:");
  655.     
  656.     this.captureExternalProtocols();
  657.     
  658.     for each(var p in [
  659.       "autoAllow",
  660.       "allowClipboard", "allowLocalLinks",
  661.       "allowedMimeRegExp", "hideOnUnloadRegExp", "requireReloadRegExp",
  662.       "blockNSWB",
  663.       "consoleDump", "consoleLog", "contentBlocker",
  664.       "docShellJSBlocking",
  665.       "filterXPost", "filterXGet", 
  666.       "filterXGetRx", "filterXGetUserRx", 
  667.       "filterXExceptions",
  668.       "forbidChromeScripts",
  669.       "forbidJarDocuments", "forbidJarDocumentsExceptions",
  670.       "forbidJava", "forbidFlash", "forbidSilverlight", "forbidPlugins", "forbidMedia", "forbidFonts",
  671.       "forbidIFrames", "forbidIFramesContext", "forbidFrames", "forbidData",
  672.       "forbidMetaRefresh",
  673.       "forbidXBL", "forbidXHR",
  674.       "inclusionTypeChecking",
  675.       "alwaysBlockUntrustedContent",
  676.       "global", "ignorePorts",
  677.       "injectionCheck", "injectionCheckSubframes",
  678.       "jsredirectIgnore", "jsredirectFollow", "jsredirectForceShow",
  679.       "jsHack", "jsHackRegExp",
  680.       "emulateFrameBreak",
  681.       "nselNever", "nselForce",
  682.       "clearClick", "clearClick.exceptions", "clearClick.subexceptions", "opaqueObject",
  683.       "showPlaceholder", "showUntrustedPlaceholder", "collapseObject", "abp.removeTabs",
  684.       "temp", "untrusted", "gtemp",
  685.       "flashPatch", "silverlightPatch",
  686.       "secureCookies", "secureCookiesExceptions", "secureCookiesForced",
  687.       "httpsForced", "httpsForcedExceptions", "allowHttpsOnly",
  688.       "truncateTitle", "truncateTitleLen",
  689.       "whitelistRegExp", "proxiedDNS", "asyncNetworking",
  690.       "ABE.enabled", "ABE.legacySupport", "ABE.siteEnabled", "ABE.allowRulesetRedir", "ABE.disabledRulesetNames", "ABE.skipBrowserRequests",
  691.       "STS.enabled"
  692.       ]) {
  693.       try {
  694.         this.syncPrefs(this.prefs, p);
  695.       } catch(e) {
  696.         dump("[NoScript init error] " + e.message + ":" + e.stack + " setting " + p + "\n");
  697.       }
  698.     }
  699.     
  700.     DNS.logEnabled = this.getPref("logDNS");
  701.     
  702.     this.setupJSCaps();
  703.     
  704.     // locking management
  705.     if ((this.locked = this.prefs.prefIsLocked("default"))) {
  706.       try {
  707.         const psKey = this.POLICY_NAME + ".sites";
  708.         const dc = this.defaultCaps;
  709.         dc.lockPref("policynames");
  710.         dc.unlockPref(psKey);
  711.         this.resetDefaultSitePrefs();
  712.         dc.setCharPref(psKey, this.policyPB.getCharPref("sites"));
  713.         dc.lockPref(psKey);
  714.         if (dc instanceof PBI) dc.addObserver("default.javascript.", this, true);
  715.         dc.setCharPref("default.javascript.enabled", "noAccess");
  716.         dc.lockPref("default.javascript.enabled");
  717.         this.prefs.lockPref("global");
  718.       } catch(e) {
  719.         this.dump(e);
  720.       }
  721.     } else {
  722.       // init jsPolicySites from prefs
  723.       this.syncPrefs(this.policyPB, "sites");
  724.     }
  725.     
  726.     this.syncPrefs(this.mozJSPref, "enabled");
  727.     
  728.     if (this.getPref("tempGlobal", false))
  729.       this.jsEnabled = false;
  730.     
  731.     this.eraseTemp();
  732.     
  733.     this.reloadWhereNeeded(); // init snapshot
  734.     
  735.     this.savePrefs(true); // flush preferences to file
  736.  
  737.     // hook on redirections (non persistent, otherwise crashes on 1.8.x)
  738.     CC['@mozilla.org/categorymanager;1'].getService(CI.nsICategoryManager)
  739.       .addCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, SERVICE_CTRID, false, true);
  740.         
  741.     return true;
  742.   },
  743.   
  744.   
  745.   
  746.   dispose: function() {
  747.     try {
  748.       if(!this._inited) return;
  749.       this._inited = false;
  750.       
  751.       this.shouldLoad = this.shouldProcess = CP_NOP;
  752.       
  753.       CC['@mozilla.org/categorymanager;1'].getService(CI.nsICategoryManager)
  754.         .deleteCategoryEntry("net-channel-event-sinks", SERVICE_CTRID, SERVICE_CTRID, false);
  755.       
  756.       
  757.       if (this.requestWatchdog) {
  758.         this.requestWatchdog.dispose();
  759.         this.requestWatchdog = null;
  760.       }
  761.       
  762.       OS.removeObserver(this, "em-action-requested");
  763.             
  764.       const dls = CC['@mozilla.org/docloaderservice;1'].getService(CI.nsIWebProgress);
  765.       dls.removeProgressListener(this);
  766.       
  767.       this.prefs.removeObserver("", this);
  768.       this.mozJSPref.removeObserver("enabled", this);
  769.       this.resetJSCaps();
  770.       PolicyState.reset();
  771.       
  772.       if (this.placesPrefs) this.placesPrefs.dispose();
  773.       
  774.       STS.dispose();
  775.       
  776.       if(this.consoleDump & LOG_LEAKS) this.reportLeaks();
  777.     } catch(e) {
  778.       this.dump(e + " while disposing.");
  779.     }
  780.     
  781.   },
  782.   
  783.  
  784.   onVersionChanged: function(prev) {
  785.     // upgrade hacks
  786.     
  787.     if (prev >= "1.9.3.4" && prev < "1.9.4") {
  788.       this.setPref("ABE.legacySupport", true);
  789.     } else if (prev >= "1.9.6" && prev <= "1.9.6.8") { // early 1.9.6 broke default whitelist
  790.       if (!this.isJSEnabled("about:credits") && !this.getPref("untrusted")) {
  791.         this.setJSEnabled(this.splitList(this.getPref("default")), true);
  792.       }
  793.     }
  794.     
  795.     // remove every file://host entry from the policy sites lists
  796.     var rx = /\bfile:\/\/\S+\s*/g;
  797.     if (rx.test(this.jsPolicySites.sitesString)) try {
  798.       this.flushCAPS(this.jsPolicySites.sitesString.replace(rx, ''));
  799.       this.persistUntrusted(this.untrustedSites.sitesString.replace(rx, ''));
  800.     } catch(e) {
  801.       ns.dump(e);
  802.     }
  803.     
  804.   },
  805.  
  806.   reportLeaks: function() {
  807.     // leakage detection
  808.     this.dump("DUMPING " + this.__parent__);
  809.     for(var v in this.__parent__) {
  810.       this.dump(v + " = " + this.__parent__[v] + "\n");
  811.     }
  812.   },
  813.   
  814.   captureExternalProtocols: function() {
  815.     try {
  816.       const pb = this.prefService.getDefaultBranch("network.protocol-handler.");
  817.       if (this.getPref("fixURI", true)) {
  818.         try {
  819.           pb.setBoolPref("expose-all", true);
  820.         } catch(e1) {}
  821.         var prots = [];
  822.         for each(var key in pb.getChildList("expose.", {})) {
  823.           try {
  824.             pb.setBoolPref(key, true);
  825.             prots.push(key.replace("expose.", ""));
  826.             if (pb.prefHasUserValue(key)) pb.clearUserPref(key);
  827.           } catch(e1) {}
  828.         }
  829.         if (prots.length) this.extraCapturedProtocols = prots;
  830.       }
  831.     } catch(e) {}
  832.   },
  833.   
  834.   extraCapturedProtocols: null,
  835.   
  836.   mandatorySites: new PolicySites(),
  837.   isMandatory: function(s) {
  838.     return s && this.mandatorySites.matches(s);
  839.   }
  840. ,
  841.   tempSites: new PolicySites(),
  842.   gTempSites: new PolicySites(),
  843.   isTemp: function(s) {
  844.     return s in (this.globalJS ? this.gTempSites : this.tempSites).sitesMap;
  845.   }
  846. ,
  847.   setTemp: function(s, b) {
  848.     const sites = {
  849.       "temp": this.tempSites,
  850.       "gtemp": this.gTempSites
  851.     };
  852.     for (var p in sites) {
  853.       if (b
  854.           ? (p[0] != "g" || this.globalJS) && sites[p].add(s)
  855.           : sites[p].remove(s, true, true) // keeps up and down, see #eraseTemp() 
  856.       ) sites[p].toPref(this.prefs, p);
  857.     }
  858.     return b;
  859.   },
  860.   
  861.   untrustedSites: new PolicySites(),
  862.   isUntrusted: function(s) {
  863.     return !!this.untrustedSites.matches(s);
  864.   },
  865.   setUntrusted: function(s, b) {
  866.     var change = b ? this.untrustedSites.add(s) : this.untrustedSites.remove(s, false, true);
  867.     if (change) {
  868.       this.persistUntrusted();
  869.     }
  870.     return b;
  871.   },
  872.   persistUntrusted: function(snapshot) {
  873.     if (typeof(snaposhot) == "string") {
  874.       this.untrustedSites.sitesString = snapshot;
  875.     }
  876.     this.untrustedSites.toPref(this.prefs, "untrusted");
  877.   },
  878.   
  879.   manualSites: new PolicySites(),
  880.   isManual: function(s) {
  881.     return !!this.manualSites.matches(s);
  882.   },
  883.   setManual: function(s, b) {
  884.     if (b) this.manualSites.add(s);
  885.     else this.manualSites.remove(s, true);
  886.     return b;
  887.   },
  888.   
  889.   autoTemp: function(site) {
  890.     if (!(this.isUntrusted(site) || this.isManual(site) || this.isJSEnabled(site))) {
  891.       this.setTemp(site, true);
  892.       this.setJSEnabled(site, true);
  893.       return true;
  894.     }
  895.     return false;
  896.   }
  897. ,
  898.   mustCascadeTrust: function(sites, temp) {
  899.     var untrustedGranularity = this.getPref("untrustedGranularity", 3);
  900.     /*  noscript.untrustedGranularity  controls how manually whitelisting
  901.         a domain affects the untrusted blacklist status of descendants:
  902.         0 - always delist descendants from the untrusted blacklist
  903.         1 - keep descendants blacklisted for temporary allow actions
  904.         2 - keep descendants blacklisted for permanent allow actions
  905.         3 - (default) always keep descendants blacklisted
  906.         4 - delist blacklisted descendants of a site marked as untrusted
  907.         All these values can be put in OR (the 3 default is actually 2 | 1)
  908.     */
  909.     var single = !(typeof(site) == "object" && ("push" in site)); // not an array
  910.     return !((untrustedGranularity & 1) && !temp || (untrustedGranularity & 2) && temp)
  911.       || (untrustedGranularity & 4) && single && this.isUntrusted(site);
  912.   }
  913. ,
  914.   isForbiddenByHttpsStatus: function(s) {
  915.     return HTTPS.allowHttpsOnly && HTTPS.shouldForbid(s);
  916.   }
  917. ,
  918.   jsPolicySites: new PolicySites(),
  919.   isJSEnabled: function(s) {
  920.     return !(this.globalJS
  921.       ? this.alwaysBlockUntrustedContent && this.untrustedSites.matches(s)
  922.       : !this.jsPolicySites.matches(s) || this.untrustedSites.matches(s)
  923.         || this.isForbiddenByHttpsStatus(s)
  924.     );
  925.   },
  926.   setJSEnabled: function(site, is, fromScratch, cascadeTrust) {
  927.         
  928.     const ps = this.jsPolicySites;
  929.     if (fromScratch) ps.sitesString = this.mandatorySites.sitesString;
  930.     if (is) {
  931.       ps.add(site);
  932.       if (!fromScratch) {
  933.         if (this.untrustedSites.remove(site, false, !cascadeTrust)) 
  934.           this.persistUntrusted();
  935.         
  936.         this.setManual(site, false);
  937.       }
  938.     } else {
  939.       ps.remove(site, false, true);
  940.       if (this.forbidImpliesUntrust) {
  941.         this.setUntrusted(site, true);
  942.       } else {
  943.         this.setManual(site, true);
  944.       }
  945.     }
  946.     
  947.     this.flushCAPS();
  948.     
  949.     return is;
  950.   },
  951.   
  952.   get forbidImpliesUntrust() {
  953.     return this.globalJS || this.autoAllow || this.getPref("forbidImpliesUntrust", false);
  954.   }
  955. ,
  956.   checkShorthands: function(site, map) {
  957.     if (this.whitelistRegExp && this.whitelistRegExp.test(site)) {
  958.       return true;
  959.     }
  960.     
  961.     map = map || this.jsPolicySites.sitesMap;
  962.     
  963.     if (/:\d+$/.test(site)) {
  964.       if (this.ignorePorts) {
  965.         // default match ignoring port
  966.         if (this.isJSEnabled(site.replace(/:\d+$/, ''))) return true;
  967.       } else {
  968.         // port matching, with "0" as port wildcard  and * as nth level host wildcard
  969.         var key = site.replace(/\d+$/, "0");
  970.         if (key in map || site in map) return true;
  971.         var keys = site.split(".");
  972.         if (keys.length > 1) {
  973.           var prefix = keys[0].match(/^https?:\/\//i)[0] + "*.";
  974.           while (keys.length > 2) {
  975.             keys.shift();
  976.             key = prefix + keys.join(".");
  977.             if (key in map || key.replace(/\d+$/, "0") in map) return true;
  978.           }
  979.         }
  980.       }
  981.     }
  982.     // check IP leftmost portion up to 2nd byte (e.g. [http://]192.168 or [http://]10.0.0)
  983.     var m = site.match(/^(https?:\/\/)((\d+\.\d+)\.\d+)\.\d+(?::\d|$)/);
  984.     return m && (m[2] in map || m[3] in map || (m[1] + m[2]) in map || (m[1] + m[3]) in map);
  985.   }
  986. ,
  987.   flushCAPS: function(sitesString) {
  988.     const ps = this.jsPolicySites;
  989.     if (sitesString) ps.sitesString = sitesString;
  990.     
  991.     // dump("Flushing " + ps.sitesString);
  992.     ps.toPref(this.policyPB);
  993.   }
  994. ,
  995.   get injectionChecker() {
  996.     return InjectionChecker;
  997.   }
  998. ,
  999.   splitList: function(s) {
  1000.     return s?/^[,\s]*$/.test(s)?[]:s.split(/\s*[,\s]\s*/):[];
  1001.   }
  1002. ,
  1003.   get placesPrefs() {
  1004.     if (!this.getPref("placesPrefs", false)) return null; 
  1005.     delete this.placesPrefs;
  1006.     try {
  1007.       INCLUDE('PlacesPrefs');
  1008.       PlacesPrefs.init();
  1009.     } catch(e) {
  1010.       PlacesPrefs = null;
  1011.     }
  1012.     return this.placesPrefs = PlacesPrefs;
  1013.   },
  1014.  
  1015.   savePrefs: function(skipPlaces) {
  1016.     var res = this.prefService.savePrefFile(null);
  1017.     if (this.placesPrefs && !skipPlaces) this.placesPrefs.save();
  1018.     return res;
  1019.   },
  1020.  
  1021.   sortedSiteSet: function(s) { return  SiteUtils.sortedSet(s); }
  1022. ,
  1023.   globalJS: false,
  1024.   get jsEnabled() {
  1025.     try {
  1026.       return this.mozJSEnabled && this.caps.getCharPref("default.javascript.enabled") != "noAccess";
  1027.     } catch(ex) {
  1028.       return this.uninstalling ? this.mozJSEnabled : (this.jsEnabled = this.globalJS);
  1029.     }
  1030.   }
  1031. ,
  1032.   set jsEnabled(enabled) {
  1033.     if (this.locked || this.prefs.prefIsLocked("global")) {
  1034.       enabled = false;
  1035.     }
  1036.     const prefName = "default.javascript.enabled";
  1037.     try {
  1038.       this.caps.clearUserPref("default.javascript.enabled");
  1039.     } catch(e) {}
  1040.     this.defaultCaps.setCharPref(prefName, enabled ? "allAccess" : "noAccess");
  1041.     
  1042.     this.setPref("global", enabled);
  1043.     if (enabled) {
  1044.       this.mozJSPref.setBoolPref("enabled", true);
  1045.     }
  1046.     return enabled;
  1047.   }
  1048. ,
  1049.   getSite: function(url) {
  1050.     return SiteUtils.getSite(url);
  1051.   },
  1052.   
  1053.   getQuickSite: function(url, level) {
  1054.     var site = null;
  1055.     if (level > 0 && !this.jsEnabled) {
  1056.       site = this.getSite(url);
  1057.       var domain;
  1058.       if (level > 1 && (domain = this.getDomain(site))) {
  1059.         site = level > 2 ? this.getBaseDomain(domain) : domain;
  1060.       }
  1061.     }
  1062.     return site;
  1063.   },
  1064.   
  1065.   get preferredSiteLevel() {
  1066.     return this.getPref("showAddress", false) ? 1 : this.getPref("showDomain", false) ? 2 : 3;
  1067.   },
  1068.   
  1069.   
  1070.   getDomain: function(site, force) {
  1071.     try {
  1072.       const url = (site instanceof CI.nsIURL) ? site : IOS.newURI(site, null, null);
  1073.       const host = url.host;
  1074.       return force || (this.ignorePorts || url.port == -1) && host[host.length - 1] != "." && 
  1075.             (host.lastIndexOf(".") > 0 || host == "localhost") ? host : '';
  1076.     } catch(e) {
  1077.       return '';
  1078.     }
  1079.   },
  1080.   
  1081.   get _tldService() {
  1082.     var srv = null;
  1083.     try {
  1084.       if (CI.nsIEffectiveTLDService) {
  1085.         var srv = CC["@mozilla.org/network/effective-tld-service;1"]
  1086.                 .getService(CI.nsIEffectiveTLDService);
  1087.         if (typeof(srv.getBaseDomainFromHost) != "function"
  1088.               || srv.getBaseDomainFromHost("bbc.co.uk") != "bbc.co.uk" // check, some implementations are "fake" (e.g. Songbird's)
  1089.           ) {
  1090.           srv = null;
  1091.         }
  1092.       }
  1093.       if (!srv) {
  1094.         INCLUDE('EmulatedTLDService');
  1095.         srv = EmulatedTLDService;
  1096.       }
  1097.     } catch(ex) {
  1098.       this.dump(ex);
  1099.       return null;
  1100.     }
  1101.     delete this._tldService;
  1102.     return this._tldService = srv;
  1103.   },
  1104.   
  1105.  
  1106.   getBaseDomain: function(domain) {
  1107.     if (!domain || DNS.isIP(domain)) return domain; // IP
  1108.     
  1109.     var pos = domain.lastIndexOf('.');
  1110.     if (pos < 1 || (pos = domain.lastIndexOf('.', pos - 1)) < 1) return domain;
  1111.     
  1112.     try {
  1113.       return this._tldService.getBaseDomainFromHost(domain);
  1114.     } catch(e) {
  1115.       this.dump(e);
  1116.     }
  1117.     return domain;
  1118.   },
  1119.   getPublicSuffix: function(domain) {
  1120.     try {
  1121.       if (!DNS.isIP(domain))
  1122.         return this._tldService.getPublicSuffixFromHost(domain);
  1123.     } catch(e) {}
  1124.     return "";
  1125.   }
  1126. ,
  1127.   delayExec: function(callback, time) {
  1128.     Thread.delay(callback, time, this, Array.prototype.slice.call(arguments, 2));
  1129.   }
  1130. ,
  1131.   RELOAD_NO: -1,
  1132.   RELOAD_CURRENT: 1,
  1133.   RELOAD_ALL: 0,
  1134.   safeCapsOp: function(callback, reloadPolicy, nosave) {
  1135.     this.delayExec(function() {
  1136.       try {
  1137.         callback(this);
  1138.         if (!nosave) this.savePrefs();
  1139.         if (reloadPolicy != this.RELOAD_NO) {
  1140.           this.reloadWhereNeeded(reloadPolicy == this.RELOAD_CURRENT);
  1141.         }
  1142.       } catch(e) {
  1143.         this.dump("FAILED TO SAVE PERMISSIONS! " + e + "," + e.stack);
  1144.       }
  1145.      }, 0);
  1146.   }
  1147. ,
  1148.   _lastSnapshot: {
  1149.     trusted: null,
  1150.     untrusted: null,
  1151.     global: false,
  1152.     objects: null
  1153.   },
  1154.   reloadWhereNeeded: function(currentTabOnly) {
  1155.     const trusted = this.jsPolicySites;
  1156.     const untrusted = this.untrustedSites;
  1157.     const global = this.jsEnabled;
  1158.     
  1159.     var snapshot = this._lastSnapshot;
  1160.     var lastTrusted = snapshot.trusted;
  1161.     var lastUntrusted = snapshot.untrusted;
  1162.     var lastGlobal = snapshot.global;
  1163.     var lastObjects = snapshot.objects || this.objectWhitelist;
  1164.     
  1165.     snapshot.global = global;
  1166.     snapshot.trusted = trusted.clone();
  1167.     snapshot.untrusted = untrusted.clone();
  1168.     snapshot.objects = this.objectWhitelist;
  1169.     
  1170.     this.initContentPolicy();
  1171.     
  1172.     if (!lastTrusted ||
  1173.         
  1174.         global == lastGlobal &&
  1175.         lastObjects == this.objectWhitelist && 
  1176.         trusted.equals(lastTrusted) &&
  1177.         untrusted.equals(lastUntrusted) ||
  1178.         
  1179.         !this.getPref("autoReload") ||
  1180.         
  1181.         global != lastGlobal && !this.getPref("autoReload.global")
  1182.         
  1183.         )
  1184.       return;
  1185.     
  1186.     currentTabOnly = currentTabOnly || !this.getPref("autoReload.allTabs") ||
  1187.       global != lastGlobal && !this.getPref("autoReload.allTabsOnGlobal");
  1188.     
  1189.     var useHistory = this.getPref("xss.reload.useHistory", false);
  1190.     var useHistoryExceptCurrent = this.getPref("xss.reload.useHistory.exceptCurrent", true);
  1191.       
  1192.     var docSites, site;
  1193.     var prevStatus, currStatus;
  1194.     
  1195.     var webNav, url;
  1196.     const nsIWebNavigation = CI.nsIWebNavigation;
  1197.     const nsIURL = CI.nsIURL;
  1198.     const LOAD_FLAGS = nsIWebNavigation.LOAD_FLAGS_NONE;
  1199.  
  1200.     const untrustedReload = !this.getPref("xss.trustReloads", false);
  1201.  
  1202.     var bi = DOM.createBrowserIterator();
  1203.   
  1204.     (function checkAndReload() {
  1205.       var browser, j, k;
  1206.       
  1207.       for (var k = 10; k-- > 0 && (browser = bi.next()); ) {
  1208.         
  1209.         docSites = this.getSites(browser);
  1210.   
  1211.         for (j = docSites.length; j-- > 0;) {
  1212.           site = docSites[j];
  1213.           prevStatus = !(lastGlobal
  1214.             ? this.alwaysBlockUntrustedContent && lastUntrusted.matches(site)
  1215.             : !lastTrusted.matches(site) || lastUntrusted.matches(site)
  1216.           );
  1217.           currStatus = this.isJSEnabled(site) || !!this.checkShorthands(site);
  1218.   
  1219.           if (currStatus != prevStatus) {
  1220.             if (currStatus) 
  1221.               this.requestWatchdog.setUntrustedReloadInfo(browser, true);
  1222.             
  1223.             webNav = browser.webNavigation;
  1224.             url = webNav.currentURI;
  1225.             if (url.schemeIs("http") || url.schemeIs("https")) {
  1226.               this.requestWatchdog.noscriptReload = url.spec;
  1227.             }
  1228.             try {
  1229.               webNav = webNav.sessionHistory.QueryInterface(nsIWebNavigation);
  1230.               if (currStatus && webNav.index && untrustedReload) {
  1231.                 try {
  1232.                   site = this.getSite(webNav.getEntryAtIndex(webNav.index - 1, false).URI.spec);
  1233.                   this.requestWatchdog.setUntrustedReloadInfo(browser, site != docSites[j] && !trusted.matches(site));
  1234.                 } catch(e) {}
  1235.               }
  1236.               
  1237.               if (useHistory) {
  1238.                 if (useHistoryExceptCurrent) {
  1239.                   useHistoryExceptCurrent = false;
  1240.                 } else if(!(url instanceof nsIURL && url.ref || url.spec.substring(url.spec.length - 1) == "#")) {
  1241.                   if (useHistoryCurrentOnly) useHistory = false;
  1242.                   webNav.gotoIndex(webNav.index);
  1243.                   break;
  1244.                 }
  1245.               }
  1246.             } catch(e) {}
  1247.             try {
  1248.               browser.webNavigation.reload(LOAD_FLAGS); // can fail, e.g. because a content policy or an user interruption
  1249.             } catch(e) {}
  1250.             break;
  1251.           }
  1252.         }
  1253.         
  1254.         if(j < 0) { 
  1255.           // check plugin objects
  1256.           if (this.consoleDump & LOG_CONTENT_BLOCK) {
  1257.             this.dump("Checking object permission changes...");
  1258.             try {
  1259.               this.dump(docSites.toSource() + ", " + lastObjects.toSource());
  1260.             } catch(e) {}
  1261.           }
  1262.           if (this.checkObjectPermissionsChange(docSites, lastObjects)) {
  1263.              this.quickReload(browser.webNavigation);
  1264.           }
  1265.         }
  1266.         
  1267.         if (currentTabOnly) {
  1268.           bi.dispose();
  1269.           return;
  1270.         }
  1271.         
  1272.         Thread.delay(checkAndReload, this, 1);
  1273.       }
  1274.     }).apply(this);
  1275.     
  1276.   },
  1277.   
  1278.   
  1279.   reloadAllowedObjects: function(browser) {
  1280.     if (this.getPref("autoReload.onMultiContent", false)) {
  1281.       this.quickReload(browser.webNavigation);
  1282.       return;
  1283.     }
  1284.     
  1285.     var sites = this.getSites(browser);
  1286.     var egroup, len, j, e;
  1287.     for each (egroup in sites.pluginExtras) {
  1288.       for (j = 0, len = egroup.length; j < len; j++) {
  1289.         e = egroup[j];
  1290.         if (this.isAllowedObject(e.url, e.mime, e.site)) {
  1291.           if (e.placeholder) {
  1292.             e.skipConfirmation = true;
  1293.             this.checkAndEnablePlaceholder(e.placeholder);
  1294.           } else if (!e.embed) {
  1295.             if (e.document) {
  1296.               this.quickReload(DOM.getDocShellForWindow(e.document.defaultView));
  1297.               break;
  1298.             } else {
  1299.               this.quickReload(browser.webNavigation);
  1300.               return;
  1301.             }
  1302.           }
  1303.         }
  1304.       }
  1305.     }
  1306.   },
  1307.   
  1308.   checkObjectPermissionsChange: function(sites, snapshot) {
  1309.     if(this.objectWhitelist == snapshot) return false;
  1310.     var s, url;
  1311.     for (url in snapshot) {
  1312.       s = this.getSite(url);
  1313.       if (!(s in snapshot)) snapshot[s] = snapshot[url];
  1314.     }
  1315.     for each (var s in sites.pluginSites) {
  1316.       s = this.objectKey(s);
  1317.       if ((s in snapshot) && !(s in this.objectWhitelist)) {
  1318.         return true;
  1319.       }
  1320.     }
  1321.     var egroup, len, j, e;
  1322.      for each (egroup in sites.pluginExtras) {
  1323.       for (j = 0, len = egroup.length; j < len; j++) {
  1324.         e = egroup[j];
  1325.         if (!e.placeholder && e.url && ((url = this.objectKey(e.url)) in snapshot) && !(url in this.objectWhitelist)) {
  1326.           return true;
  1327.         }
  1328.       }
  1329.     }
  1330.     return false;
  1331.   },
  1332.   
  1333.   quickReload: function(webNav, checkNullCache) {
  1334.     if (!(webNav instanceof CI.nsIWebNavigation)) {
  1335.       webNav = DOM.getDocShellForWindow(webNav);
  1336.     }
  1337.     
  1338.     var uri = webNav.currentURI;
  1339.     
  1340.     if (checkNullCache && (webNav instanceof CI.nsIWebPageDescriptor)) {
  1341.       try {
  1342.         var ch = IOS.newChannel(uri.spec, null, null);
  1343.         if (ch instanceof CI.nsICachingChannel) {
  1344.           ch.loadFlags |= ch.LOAD_ONLY_FROM_CACHE;
  1345.           ch.cacheKey = webNav.currentDescriptor.QueryInterface(CI.nsISHEntry).cacheKey
  1346.           if (ch.open().available() == 0) {
  1347.             webNav.reload(webNav.LOAD_FLAGS_BYPASS_CACHE);
  1348.             return;
  1349.           }
  1350.         }
  1351.       } catch(e) {
  1352.         if (this.consoleDump) this.dump(e);
  1353.       } finally {
  1354.         try {
  1355.           ch.close();
  1356.         } catch(e1) {}
  1357.       }
  1358.     }
  1359.     
  1360.  
  1361.     if (uri.schemeIs("http") || uri.schemeIs("https")) {
  1362.       this.requestWatchdog.noscriptReload = uri.spec;
  1363.     }
  1364.     webNav.reload(webNav.LOAD_FLAGS_CHARSET_CHANGE);
  1365.   },
  1366.   
  1367.   getPermanentSites: function() {
  1368.     var whitelist = this.jsPolicySites.clone();
  1369.     whitelist.remove(this.tempSites.sitesList, true, true);
  1370.     return whitelist;
  1371.   },
  1372.   
  1373.   eraseTemp: function() {
  1374.     // remove temporary PUNCTUALLY: 
  1375.     // keeps ancestors because the may be added as permanent after the temporary allow;
  1376.     // keeps descendants because they may already have been made permanent before the temporary, and then shadowed
  1377.     this.jsPolicySites.remove(this.tempSites.sitesList, true, true);
  1378.     // if allowed in blacklist mode, put back temporarily allowed in blacklist
  1379.     if (this.untrustedSites.add(this.gTempSites.sitesList)) {
  1380.       this.persistUntrusted();
  1381.     }
  1382.     
  1383.     this.setPref("temp", ""); 
  1384.     this.setPref("gtemp", "");
  1385.     
  1386.     this.setJSEnabled(this.mandatorySites.sitesList, true); // add mandatory
  1387.     this.resetAllowedObjects();
  1388.     if (this.clearClickHandler) this.clearClickHandler.resetWhitelist();
  1389.   }
  1390.   
  1391. ,
  1392.   _observingPolicies: false,
  1393.   _editingPolicies: false,
  1394.   setupJSCaps: function() {
  1395.     if (this._editingPolicies) return;
  1396.     this._editingPolicies = true;
  1397.     try {
  1398.       const POLICY_NAME = this.POLICY_NAME;
  1399.       var prefArray;
  1400.       var prefString = "", originalPrefString = "";
  1401.       var exclusive = this.getPref("excaps", true);
  1402.       try {
  1403.         
  1404.         prefArray = this.splitList(prefString = originalPrefString = 
  1405.           (this.caps.prefHasUserValue("policynames") 
  1406.             ? this.caps.getCharPref("policynames")
  1407.             : this.getPref("policynames") // saved value from dirty exit
  1408.           )
  1409.         );
  1410.         var pcount = prefArray.length;
  1411.         while (pcount-- > 0 && prefArray[pcount] != POLICY_NAME);
  1412.         if (pcount == -1) { // our policy is not installed, should always be so unless dirty exit
  1413.           this.setPref("policynames", originalPrefString);
  1414.           if (exclusive || prefArray.length == 0) {
  1415.             prefString = POLICY_NAME;
  1416.           } else {
  1417.             prefArray.push(POLICY_NAME);
  1418.             prefString = prefArray.join(' ');
  1419.           }
  1420.         }
  1421.         prefString = prefString.replace(/,/g, ' ').replace(/\s+/g, ' ').replace(/^\s+/, '').replace(/\s+$/, '');
  1422.       } catch(ex) {
  1423.         prefString = POLICY_NAME;
  1424.       }
  1425.       
  1426.       this.caps.setCharPref(POLICY_NAME + ".javascript.enabled", "allAccess");
  1427.       
  1428.       try {
  1429.         this.caps.clearUserPref("policynames");
  1430.       } catch(e) {}
  1431.       this.defaultCaps.setCharPref("policynames", prefString);
  1432.  
  1433.       
  1434.       if (!this._observingPolicies) {
  1435.         this.caps.addObserver("policynames", this, true);
  1436.         this._observingPolicies = true;
  1437.       }
  1438.     } catch(ex) {
  1439.       dump(ex.message);
  1440.     }
  1441.     this._editingPolicies = false;
  1442.   },
  1443.   resetJSCaps: function() {
  1444.     try {
  1445.       this.caps.clearUserPref("default.javascript.enabled");
  1446.     } catch(ex) {}
  1447.     if (this._observingPolicies) {
  1448.       this.caps.removeObserver("policynames", this, false);
  1449.       this._observingPolicies = false;
  1450.     }
  1451.     try {
  1452.       const POLICY_NAME = this.POLICY_NAME;
  1453.       var prefArray = SiteUtils.splitString(
  1454.         this.getPref("excaps", true) ? this.getPref("policynames", "") : this.caps.getCharPref("policynames")
  1455.       );
  1456.       var pcount = prefArray.length;
  1457.       const prefArrayTarget = [];
  1458.       for (var pcount = prefArray.length; pcount-- > 0;) {
  1459.         if (prefArray[pcount] != POLICY_NAME) prefArrayTarget[prefArrayTarget.length] = prefArray[pcount];
  1460.       }
  1461.       var prefString = prefArrayTarget.join(" ").replace(/\s+/g,' ').replace(/^\s+/,'').replace(/\s+$/,'');
  1462.       if (prefString) {
  1463.         this.caps.setCharPref("policynames", prefString);
  1464.       } else {
  1465.         try {
  1466.           this.caps.clearUserPref("policynames");
  1467.         } catch(ex1) {}
  1468.       }
  1469.       try {
  1470.         this.clearUserPref("policynames");
  1471.       } catch(ex1) {}
  1472.       
  1473.       this.eraseTemp();
  1474.       this.savePrefs(true);
  1475.     } catch(ex) {}
  1476.   }
  1477. ,
  1478.   uninstallJob: function() {
  1479.     // this.resetJSCaps();
  1480.   },
  1481.   undoUninstallJob: function() {
  1482.     // this.setupJSCaps();
  1483.   }
  1484. ,
  1485.   getPref: function(name, def) {
  1486.     const IPC = CI.nsIPrefBranch;
  1487.     const prefs = this.prefs;
  1488.     try {
  1489.       switch (prefs.getPrefType(name)) {
  1490.         case IPC.PREF_STRING:
  1491.           return prefs.getCharPref(name);
  1492.         case IPC.PREF_INT:
  1493.           return prefs.getIntPref(name);
  1494.         case IPC.PREF_BOOL:
  1495.           return prefs.getBoolPref(name);
  1496.       }
  1497.     } catch(e) {}
  1498.     return def || "";
  1499.   }
  1500. ,
  1501.   setPref: function(name, value) {
  1502.     const prefs = this.prefs;
  1503.     try {
  1504.       switch (typeof(value)) {
  1505.         case "string":
  1506.             prefs.setCharPref(name,value);
  1507.             break;
  1508.         case "boolean":
  1509.           prefs.setBoolPref(name,value);
  1510.           break;
  1511.         case "number":
  1512.           prefs.setIntPref(name,value);
  1513.           break;
  1514.         default:
  1515.           throw new Error("Unsupported type " + typeof(value) + " for preference "+name);
  1516.       }
  1517.     } catch(e) {
  1518.       const IPC = CI.nsIPrefBranch;
  1519.       try {
  1520.         switch (prefs.getPrefType(name)) {
  1521.           case IPC.PREF_STRING:
  1522.             prefs.setCharPref(name, value);
  1523.             break;
  1524.           case IPC.PREF_INT:
  1525.             prefs.setIntPref(name, parseInt(value));
  1526.             break;
  1527.           case IPC.PREF_BOOL:
  1528.             prefs.setBoolPref(name, !!value && value != "false");
  1529.             break;
  1530.         }
  1531.       } catch(e2) {}
  1532.     }
  1533.   },
  1534.   
  1535.   get json() {
  1536.     delete this.json;
  1537.     var json;
  1538.     try {
  1539.       json = CC["@mozilla.org/dom/json;1"].createInstance(CI.nsIJSON);
  1540.     } catch(e) {
  1541.       json = null;
  1542.     }
  1543.     return this.json = json;
  1544.   },
  1545.   
  1546.   get placesSupported() {
  1547.     return "nsINavBookmarksService" in Components.interfaces;
  1548.   },
  1549.   
  1550.   get canSerializeConf() { return !!this.json },
  1551.   serializeConf: function(beauty) {
  1552.     if (!this.json) return '';
  1553.     
  1554.     var exclude = ["version", "temp", "placesPrefs.ts"];
  1555.     var prefs = {};
  1556.     for each (var key in this.prefs.getChildList("", {})) {
  1557.       if (exclude.indexOf(key) < 0) {
  1558.         prefs[key] = this.getPref(key);
  1559.       }
  1560.     }
  1561.     
  1562.     var conf = this.json.encode({
  1563.       prefs: prefs,
  1564.       whitelist: this.getPermanentSites().sitesString,
  1565.       ABE: ABE.serialize(),
  1566.       V: this.VERSION
  1567.     });
  1568.     
  1569.     return beauty ? conf.replace(/([^\\]"[^"]*[,\{])"/g, "$1\r\n\"").replace(/},?(?:\n|$)/g, "\r\n$&") : conf;
  1570.   },
  1571.   
  1572.   restoreConf: function(s) {
  1573.     try {
  1574.       var json = this.json.decode(s.replace(/[\n\r]/g, ''));
  1575.       if (json.ABE) ABE.restore(json.ABE);
  1576.       var prefs = json.prefs;
  1577.       for (var key in prefs) this.setPref(key, prefs[key]);
  1578.  
  1579.       this.flushCAPS(json.whitelist);
  1580.       this.setPref("temp", ""); 
  1581.       this.setPref("gtemp", "");
  1582.       
  1583.       return true;
  1584.     } catch(e) {
  1585.       this.dump("Cannot restore configuration: " + e);
  1586.       return false;
  1587.     }
  1588.   }  
  1589. ,
  1590.   _sound: null,
  1591.   playSound: function(url, force) {
  1592.     if (force || this.getPref("sound", false)) {
  1593.       var sound = this._sound;
  1594.       if (sound == null) {
  1595.         sound = CC["@mozilla.org/sound;1"].createInstance(CI.nsISound);
  1596.         sound.init();
  1597.         this._sound = sound;
  1598.       }
  1599.       try {
  1600.         sound.play(IOS.newURI(url, null, null));
  1601.       } catch(ex) {
  1602.         //dump(ex);
  1603.       }
  1604.     }
  1605.   },
  1606.   _soundNotified: {},
  1607.   soundNotify: function(url) {
  1608.     if (this.getPref("sound.oncePerSite", true)) {
  1609.       const site = this.getSite(url);
  1610.       if (this._soundNotified[site]) return;
  1611.       this._soundNotified[site] = true;
  1612.     }
  1613.     this.playSound(this.getPref("sound.block"));
  1614.   }
  1615. ,
  1616.   readFile: function(file) {
  1617.     return IO.readFile(file);
  1618.   }
  1619. ,
  1620.   writeFile: function(file, content) {
  1621.     IO.writeFile(file, content);
  1622.   }
  1623. ,
  1624.   
  1625.   getAllowObjectMessage: function(url, mime) {
  1626.     url = SiteUtils.crop(url);
  1627.     return this.getString("allowTemp", [url + "\n(" + mime + ")\n"]);
  1628.   }
  1629. ,
  1630.   lookupMethod: DOM.lookupMethod,
  1631.   dom: DOM,
  1632.   siteUtils: SiteUtils,
  1633.  
  1634.   mimeService: null,
  1635.  
  1636.   shouldLoad: CP_NOP,
  1637.   shouldProcess: CP_NOP,
  1638.   
  1639.   initContentPolicy: function() {
  1640.     const last = this.getPref("cp.last");
  1641.     const catman = Module.categoryManager;
  1642.     const cat = "content-policy"; 
  1643.     if (last) catman.deleteCategoryEntry(cat, SERVICE_CTRID, false);
  1644.     
  1645.     var delegate = this.disabled ||
  1646.         (this.globalJS &&
  1647.           !(this.alwaysBlockUntrustedContent || this.contentBlocker || this.httpsForced))   
  1648.       ? NOPContentPolicy
  1649.       : MainContentPolicy;
  1650.       
  1651.     for (var p in delegate) this[p] = delegate[p];
  1652.     
  1653.     if (last && delegate != NOPContentPolicy) {
  1654.       // removing and adding the category late in the game allows to be the latest policy to run,
  1655.       // and nice to AdBlock Plus
  1656.       catman.addCategoryEntry(cat, SERVICE_CTRID, SERVICE_CTRID, true, true);
  1657.     }
  1658.     
  1659.     if (!this.mimeService) {
  1660.       
  1661.       this.rejectCode = typeof(/ /) == "object" ? -4 : -3;
  1662.       this.safeToplevel = this.getPref("safeToplevel", true);
  1663.       this.initSafeJSRx();
  1664.       this.mimeService = CC['@mozilla.org/uriloader/external-helper-app-service;1']
  1665.         .getService(CI.nsIMIMEService);
  1666.     }
  1667.   },
  1668.  
  1669.   
  1670.   guessMime: function(uriOrExt) {
  1671.     try {
  1672.       var ext = (uriOrExt instanceof CI.nsIURL) ? uriOrExt.fileExtension : uriOrExt;
  1673.       return ext && this.mimeService.getTypeFromExtension(ext) || "";
  1674.     } catch(e) {
  1675.       return "";
  1676.     }
  1677.   },
  1678.   
  1679.   pluginForMime: function(mimeType) {
  1680.     if (!mimeType) return null;
  1681.     try {
  1682.       var w = DOM.mostRecentBrowserWindow;
  1683.       if (!(w && w.navigator)) return null;
  1684.       var mime = w.navigator.mimeTypes.namedItem(mimeType);
  1685.       return mime && mime.enabledPlugin || null;
  1686.     } catch(e) { return null; }
  1687.   },
  1688.   
  1689.   isMediaType: function(mimeType) {
  1690.     return /^(?:vide|audi)o\/|\/ogg$/i.test(mimeType);
  1691.   },
  1692.   
  1693.   checkForbiddenChrome: function(url, origin) {
  1694.     var f, browserChromeDir, chromeRegistry;
  1695.     try {
  1696.       browserChromeDir = CC["@mozilla.org/file/directory_service;1"].getService(CI.nsIProperties)
  1697.                        .get("AChrom", CI.nsIFile);
  1698.       chromeRegistry = CC["@mozilla.org/chrome/chrome-registry;1"].getService(CI.nsIChromeRegistry);
  1699.       
  1700.       f = function(url, origin) {
  1701.         if(origin && !/^(?:chrome|resource|about)$/.test(origin.scheme)) {
  1702.           switch(url.scheme) {
  1703.             case "chrome":
  1704.               var packageName = url.host;
  1705.               if (packageName == "browser") return false; // fast path for commonest case
  1706.               exception = this.getPref("forbidChromeExceptions." + packageName, false);
  1707.               if (exception) return false;
  1708.               var chromeURL = chromeRegistry.convertChromeURL(url);
  1709.               if (chromeURL instanceof CI.nsIJARURI) 
  1710.                 chromeURL = chromeURL.JARFile;
  1711.                     
  1712.               return chromeURL instanceof CI.nsIFileURL && !browserChromeDir.contains(chromeURL.file, true);
  1713.              
  1714.             case "resource":
  1715.               if(/\.\./.test(unescape(url.spec))) return true;
  1716.           }
  1717.         }
  1718.         return false;
  1719.       }
  1720.     } catch(e) {
  1721.       f = function() { return false; }
  1722.     }
  1723.     this.checkForbiddenChrome = f;
  1724.     return this.checkForbiddenChrome(url, origin);
  1725.   },
  1726.  
  1727.   
  1728.   versionComparator: CC["@mozilla.org/xpcom/version-comparator;1"].createInstance(CI.nsIVersionComparator),
  1729.   geckoVersion: ("nsIXULAppInfo" in  Components.interfaces) ? CC["@mozilla.org/xre/app-info;1"].getService(CI.nsIXULAppInfo).platformVersion : "0.0",
  1730.   geckoVersionCheck: function(v) {
  1731.     return this.versionComparator.compare(this.geckoVersion, v);
  1732.   },
  1733.   
  1734.   
  1735.   _bug453825: true,
  1736.   _bug472495: true,
  1737.   
  1738.   POLICY_XBL: "TYPE_XBL" in CI.nsIContentPolicy,
  1739.   POLICY_OBJSUB: "TYPE_OBJECT_SUBREQUEST" in CI.nsIContentPolicy,
  1740.   
  1741.   cpConsoleFilter: [2, 5, 6, 7, 15],
  1742.   cpDump: function(msg, aContentType, aContentLocation, aRequestOrigin, aContext, aMimeTypeGuess, aInternalCall) {
  1743.     this.dump("Content " + msg + " -- type: " + aContentType + ", location: " + (aContentLocation && aContentLocation.spec) + 
  1744.       ", origin: " + (aRequestOrigin && aRequestOrigin.spec) + ", ctx: " + 
  1745.         ((aContext instanceof CI.nsIDOMHTMLElement) ? "<HTML Element>" // try not to cause side effects of toString() during load
  1746.           : aContext)  + 
  1747.         ", mime: " + aMimeTypeGuess + ", " + aInternalCall);
  1748.   },
  1749.   reject: function(what, args) {
  1750.     
  1751.     if (args.xOriginCached)
  1752.       XOriginCache.pick(args.xOriginKey, true);
  1753.     
  1754.     if (this.consoleDump) {
  1755.       if(this.consoleDump & LOG_CONTENT_BLOCK && args.length == 6) {
  1756.         this.cpDump("BLOCKED " + what, args[0], args[1], args[2], args[3], args[4], args[5]);
  1757.       }
  1758.       if(this.consoleDump & LOG_CONTENT_CALL) {
  1759.         this.dump(new Error().stack);
  1760.       }
  1761.     }
  1762.     switch(args[0]) {
  1763.       case 9:
  1764.         // our take on https://bugzilla.mozilla.org/show_bug.cgi?id=387971
  1765.         args[1].spec = this.nopXBL;
  1766.         return CP_OK;
  1767.     }
  1768.     
  1769.     PolicyState.cancel(args);
  1770.     
  1771.     this.recordBlocked(this.getSite(args[1].spec));
  1772.     
  1773.     return this.rejectCode;
  1774.   },
  1775.   
  1776.   get nopXBL() {
  1777.     const v = this.POLICY_XBL
  1778.       ? "chrome://global/content/bindings/general.xml#basecontrol"
  1779.       : this.contentBase + "noscript.xbl#nop";
  1780.     this.__defineGetter__("nopXBL", function() { return v; });
  1781.     return v;
  1782.   },
  1783.   
  1784.   _domNodeRemoved: function(ev) {
  1785.     if (ns.consoleDump) ns.dump("Removing DOMNodeRemoved listener");
  1786.     ev.currentTarget.removeEventListener(ev.type, arguments.callee, true);
  1787.   },
  1788.   
  1789.   forbiddenJSDataDoc: function(locationURL, originSite, aContext) {
  1790.      return !this.isSafeJSURL(locationURL) &&
  1791.         (this.forbidData && !this.isFirebugJSURL(locationURL) || locationURL == "javascript:") && 
  1792.         (!originSite || /^moz-nullprincipal:/.test(originSite))
  1793.           ? aContext && (
  1794.                     (aContext instanceof CI.nsIDOMWindow) 
  1795.                       ? aContext
  1796.                       : aContext.ownerDocument.defaultView
  1797.                   ).isNewToplevel
  1798.           : this.forbidData && !(this.isJSEnabled(originSite) ||
  1799.               (aContext instanceof CI.nsIDOMHTMLFrameElement ||
  1800.                aContext instanceof CI.nsIDOMHTMLIFrameElement) &&
  1801.               (
  1802.               /^data:[\w\/]+,$/.test(locationURL) ||
  1803.               this.isPluginDocumentURL(locationURL, "iframe") ||
  1804.               this.isPluginDocumentURL(locationURL, "embed") ||
  1805.               this.isPluginDocumentURL(locationURL, "video") ||
  1806.               this.isPluginDocumentURL(locationURL, "audio")
  1807.               )
  1808.             );
  1809.   },
  1810.   
  1811.   forbiddenXMLRequest: function(aRequestOrigin, aContentLocation, aContext, forbidDelegate) {
  1812.     var originURL, locationURL;
  1813.     if (aContentLocation.schemeIs("chrome") || !aRequestOrigin || 
  1814.          // GreaseMonkey Ajax comes from resource: hidden window
  1815.          // Google Toolbar Ajax from about:blank
  1816.            /^(?:chrome:|resource:|about:blank)/.test(originURL = aRequestOrigin.spec) ||
  1817.            // Web Developer extension "appears" to XHR towards about:blank
  1818.            (locationURL = aContentLocation.spec) == "about:blank"
  1819.           ) return false;
  1820.     var win = aContext && aContext.defaultView;
  1821.     if(win) {
  1822.       this.getExpando(win.top.document, "codeSites", []).push(this.getSite(locationURL));
  1823.     }
  1824.     return forbidDelegate.call(this, originURL, locationURL);
  1825.   },
  1826.   
  1827.   addFlashVars: function(url, embed) {
  1828.     // add flashvars to have a better URL ID
  1829.     if (embed instanceof CI.nsIDOMElement) try {
  1830.       var flashvars = embed.getAttribute("flashvars");
  1831.       if (flashvars) url += "#!flashvars#" + encodeURI(flashvars); 
  1832.     } catch(e) {
  1833.       if (this.consoleDump) this.dump("Couldn't add flashvars to " + url + ":" + e);
  1834.     }
  1835.     return url;
  1836.   },
  1837.   
  1838.   addObjectParams: function(url, embed) {
  1839.     if (embed instanceof CI.nsIDOMElement) try {
  1840.       var params = embed.getElementsByTagName("param");
  1841.       if(!params.length) return url;
  1842.       
  1843.       var pp = [];
  1844.       for(var j = params.length; j-- > 0;) {
  1845.         pp.push(encodeURIComponent(params[j].name) + "=" + encodeURIComponent(params[j].value));
  1846.       }
  1847.       url += "#!objparams#" + pp.join("&");
  1848.     } catch (e) {
  1849.       if (this.consoleDump) this.dump("Couldn't add object params to " + url + ":" + e);
  1850.     }
  1851.     return url;
  1852.   },
  1853.   
  1854.   tagWindowlessObject: function(o) {
  1855.     const rx = /opaque|transparent/i;
  1856.     var b;
  1857.     try {
  1858.       if (o instanceof CI.nsIDOMHTMLEmbedElement) {
  1859.         b = rx.test(o.getAttribute("wmode"));
  1860.       } else if (o instanceof CI.nsIDOMHTMLObjectElement) {
  1861.         var params = o.getElementsByTagName("param");
  1862.         for(var j = params.length; j-- > 0 &&
  1863.             !(b = /wmode/i.test(params[j].name && rx.test(params[j].value)));
  1864.         );
  1865.       }
  1866.       if (b) this.setExpando(o, "windowless", true);
  1867.     } catch (e) {
  1868.       if (this.consoleDump) this.dump("Couldn't tag object for window mode.");
  1869.     }
  1870.   },
  1871.   
  1872.   isWindowlessObject: function(o) {
  1873.     return this.getExpando(o, "windowless") || o.settings && o.settings.windowless;
  1874.   },
  1875.   
  1876.   resolveSilverlightURL: function(uri, embed) {
  1877.     if(!uri) return "";
  1878.     
  1879.     
  1880.     if (embed instanceof CI.nsIDOMElement) try {
  1881.       
  1882.       var url = "";
  1883.       var params = embed.getElementsByTagName("param");
  1884.       if (!params.length) return uri.spec;
  1885.       
  1886.       var name, value, pp = [];
  1887.       for (var j = params.length; j-- > 0;) { // iteration inverse order is important for "source"!
  1888.         name = params[j].name;
  1889.         value = params[j].value;
  1890.         if(!(name && value)) continue;
  1891.         
  1892.         if (!url && name.toLowerCase() == "source") {
  1893.           try {
  1894.              url = uri.resolve(value);
  1895.              continue;
  1896.           } catch(e) {
  1897.             if (this.consoleDump)  
  1898.               this.dump("Couldn't resolve Silverlight URL " + uri.spec + " + " + value + ":" + e);
  1899.             url = uri.spec;
  1900.           }
  1901.         }
  1902.         pp.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
  1903.       }
  1904.       return (url || uri.spec) + "#!objparams#" + pp.join("&");
  1905.     } catch(e1) {
  1906.       if (this.consoleDump)  this.dump("Couldn't resolve Silverlight URL " + uri.spec + ":" + e1);
  1907.     }
  1908.     return uri.spec;
  1909.   },
  1910.   
  1911.   tagForReplacement: function(embed, pluginExtras) {
  1912.     try {
  1913.       var doc = embed.ownerDocument;
  1914.       if(!doc) {
  1915.         if (embed instanceof CI.nsIDOMDocumentView) {
  1916.           pluginExtras.document = (doc = embed);
  1917.           pluginExtras.url = this.getSite(pluginExtras.url);
  1918.           this._collectPluginExtras(this.findPluginExtras(doc), pluginExtras);
  1919.         }
  1920.       } else {
  1921.         this.getExpando(doc, "pe",  []).push({embed: embed, pluginExtras: pluginExtras});
  1922.       }
  1923.       try {
  1924.         this.syncUI(doc.defaultView.top);
  1925.       } catch(noUIex) {
  1926.         if(this.consoleDump) this.dump(noUIex);
  1927.       }
  1928.     } catch(ex) {
  1929.       if(this.consoleDump) this.dump(
  1930.         "Error tagging object [" + pluginExtras.mime + " from " + pluginExtras.url +
  1931.         " - top window " + win + ", embed " + embed +
  1932.         "] for replacement: " + ex);
  1933.     }
  1934.   },
  1935.   
  1936.   blockLegacyFrame: function(frame, uri, sync) {
  1937.  
  1938.     var verbose = this.consoleDump & LOG_CONTENT_BLOCK;
  1939.     if(verbose) {
  1940.       this.dump("Redirecting blocked legacy frame " + uri.spec + ", sync=" + sync);
  1941.     }
  1942.     
  1943.     
  1944.     var url = this.createPluginDocumentURL(uri.spec, "iframe");
  1945.     
  1946.     if(sync) {
  1947.       if (verbose) dump("Legacy frame SYNC, setting to " + url + "\n");
  1948.       frame.contentWindow.location = url;
  1949.     } else {
  1950.       frame.ownerDocument.defaultView.addEventListener("load", function(ev) {
  1951.           if(verbose) dump("Legacy frame ON PARENT LOAD, setting to " + url + "\n");
  1952.           ev.currentTarget.removeEventListener("load", arguments.callee, false);
  1953.           frame.contentWindow.location = url;
  1954.       }, false);
  1955.     }
  1956.     return true;
  1957.   
  1958.   },
  1959.   
  1960.   
  1961.   isPluginDocumentURL: function(url, tag) {
  1962.     try {
  1963.       return url.replace(/(src%3D%22).*?%22/i, '$1%22') == this.createPluginDocumentURL('', tag)
  1964.     } catch(e) {}
  1965.     return false;
  1966.   },
  1967.   
  1968.   createPluginDocumentURL: function(url, tag) {
  1969.     tag = tag ? tag.toLowerCase() : "embed";
  1970.     return 'data:text/html;charset=utf-8,' +
  1971.         encodeURIComponent('<html><head></head><body style="padding: 0px; margin: 0px"><' +
  1972.           tag + ' src="' + url + '" width="100%" height="100%"></' +
  1973.           tag + '></body></html>');
  1974.   },
  1975.   
  1976.   forbiddenIFrameContext: function(originURL, locationURL) {
  1977.     if (this.isForbiddenByHttpsStatus(originURL)) return false;
  1978.     var domain = this.getDomain(locationURL, true);
  1979.     if (!domain) return false;
  1980.     switch (this.forbidIFramesContext) {
  1981.       case 0: // all IFRAMES
  1982.         return true;
  1983.       case 3: // different 2nd level domain
  1984.         return this.getBaseDomain(this.getDomain(originURL, true)) != 
  1985.           this.getBaseDomain(domain);
  1986.       case 2: // different domain (unless forbidden by HTTPS status)
  1987.         if (this.getDomain(originURL, true) != domain) return true;
  1988.         // if we trust only HTTPS both sites must have the same scheme
  1989.         if (!this.isForbiddenByHttpsStatus(locationURL.replace(/^https:/, 'http:'))) return false;
  1990.       case 1: // different site
  1991.         return this.getSite(originURL) != this.getSite(locationURL);
  1992.      }
  1993.      return false;
  1994.   },
  1995.   
  1996.   forbiddenXBLContext: function(originURL, locationURL) {
  1997.     if (locationURL == this.nopXBL) return false; // always allow our nop binding
  1998.     
  1999.     var locationSite = this.getSite(locationURL);
  2000.     var originSite = this.getSite(originURL);
  2001.    
  2002.     switch (this.forbidXBL) {
  2003.       case 4: // allow only XBL from the same trusted site or chrome (default)
  2004.         if (locationSite != originSite) return true; // chrome is checked by the caller checkXML
  2005.       case 3: // allow only trusted XBL on trusted sites
  2006.         if (!locationSite) return true;
  2007.       case 2: // allow trusted and data: (Fx 3) XBL on trusted sites
  2008.         if (!(this.isJSEnabled(originSite) ||
  2009.               /^file:/.test(locationURL) // we trust local files to allow Linux theming
  2010.              )) return true;
  2011.       case 1: // allow trusted and data: (Fx 3) XBL on any site
  2012.         if (!(this.isJSEnabled(locationSite) || /^(?:data|file|resource):/.test(locationURL))) return true;
  2013.       case 0: // allow all XBL
  2014.         return false;
  2015.     }
  2016.     return true;
  2017.   },
  2018.   
  2019.   forbiddenXHRContext: function(originURL, locationURL) {
  2020.     var locationSite = this.getSite(locationURL);
  2021.     // var originSite = this.getSite(originURL);
  2022.     switch (this.forbidXHR) {
  2023.       case 3: // forbid all XHR
  2024.         return true;
  2025.       case 2: // allow same-site XHR only
  2026.         if (locationSite != originSite) return true;
  2027.       case 1: // allow trusted XHR targets only
  2028.         if (!(this.isJSEnabled(locationSite))) return true;
  2029.       case 0: // allow all XBL
  2030.         return false;
  2031.     }
  2032.     return true;
  2033.   },
  2034.   
  2035.   
  2036.   safeJSRx: false,
  2037.   initSafeJSRx: function() {
  2038.     try {
  2039.       this.safeJSRx = new RegExp("^\\s*" + this.getPref("safeJSRx", "") + "\\s*;?\\s*$");
  2040.     } catch(e) {
  2041.       this.safeJSRx = false;
  2042.     }
  2043.   },
  2044.   isSafeJSURL: function(url) {
  2045.     var js = url.replace(/^javascript:/i, "");
  2046.     return this.safeJSRx && js != url && this.safeJSRx.test(js);
  2047.   },
  2048.   
  2049.   isFirebugJSURL: function(url) {
  2050.     return url == "javascript: eval(__firebugTemp__);"
  2051.   },
  2052.   
  2053.   isExternalScheme: function(scheme) {
  2054.     try {
  2055.       return IOS.getProtocolHandler(scheme).scheme != scheme;
  2056.     } catch(e) {
  2057.       return false;
  2058.     }
  2059.   },
  2060.   normalizeExternalURI: function(uri) {
  2061.     var uriSpec = uri.spec;
  2062.     var uriValid = URIValidator.validate(uriSpec);
  2063.     var fixURI = this.getPref("fixURI", true) && 
  2064.       this.getPref("fixURI.exclude", "").split(/[^\w\-]+/)
  2065.           .indexOf(uri.scheme) < 0;
  2066.     var msg;
  2067.     if (!uriValid) {
  2068.       if (fixURI) {
  2069.         uriSpec = uriSpec
  2070.             .replace(/[\s\x01-\x1f\0]/g, " ") // whitespace + null + control chars all to space
  2071.             .replace(/%[01][\da-f]/gi, "%20"); // ditto for already encoded items
  2072.         if (uriSpec != uri.spec) {
  2073.           if (this.consoleDump) this.dump("Fixing URI: " + uri.spec + " into " + uriSpec);
  2074.           if (uriValid !== false || (uriValid = URIValidator.validate(uriSpec))) {
  2075.             uri.spec = uriSpec;
  2076.           }
  2077.         }
  2078.       }
  2079.       if (uriValid === false) {
  2080.         msg = "Rejected invalid URI: " + uriSpec;
  2081.         if (this.consoleDump) this.dump(msg);
  2082.         this.log("[NoScript URI Validator] " + msg);
  2083.         return false;
  2084.       }
  2085.     }
  2086.     // encode all you can (i.e. don't touch valid encoded and delims)
  2087.     if (fixURI) {
  2088.       try {
  2089.         uriSpec = uriSpec.replace(/[^%]|%(?![\da-f]{2})/gi, encodeURI); 
  2090.         if (uriSpec != uri.spec) {
  2091.           if (this.consoleDump) this.dump("Encoded URI: " + uri.spec + " to " + uriSpec);
  2092.           uri.spec = uriSpec;
  2093.         }
  2094.       } catch(ex) {
  2095.         msg = "Error assigning encoded URI: " + uriSpec + ", " + ex;
  2096.         if (this.consoleDump) this.dump(msg);
  2097.         this.log("[NoScript URI Validator] " + msg);
  2098.         return false;
  2099.       }
  2100.     }
  2101.     return true;
  2102.   },
  2103.   
  2104.   syncUI: function(domNode) {
  2105.     const browser = DOM.findBrowserForNode(domNode);
  2106.     if (browser && (browser.docShell instanceof CI.nsIWebProgress) && !browser.docShell.isLoadingDocument) {
  2107.       var overlay = this.findOverlay(browser);
  2108.       if(overlay) overlay.syncUI(browser.contentWindow);
  2109.     }
  2110.   },
  2111.   
  2112.   objectWhitelist: {},
  2113.   ALL_TYPES: ["*"],
  2114.   objectWhitelistLen: 0,
  2115.   objectKey: function(url) {
  2116.     return IOUtil.anonymizeURL(url.replace(/^((?:\w+:\/\/)?[^\.\/\d]+)\d+(\.[^\.\/]+\.)/, '$1$2'));
  2117.   },
  2118.   anyAllowedObject: function(site, mime) {
  2119.     site = this.objectKey(site);
  2120.     if (site in this.objectWhitelist) return true;
  2121.     site += '/';
  2122.     for(var s in this.objectWhitelist) {
  2123.       if (s.indexOf(site) == 0) return true;
  2124.     }
  2125.     return false;
  2126.   },
  2127.   isAllowedObject: function(url, mime, site) {
  2128.     url = this.objectKey(url);
  2129.     var types = this.objectWhitelist[url] || null;
  2130.     if (types && (types == this.ALL_TYPES || types.indexOf(mime) > -1)) 
  2131.       return true;
  2132.     
  2133.     site = this.objectKey((arguments.length >= 3) ? site : this.getSite(url));
  2134.      
  2135.     var types, s;
  2136.     for (;;) {
  2137.  
  2138.       types = site && this.objectWhitelist[site] || null;
  2139.       if (types && (types == this.ALL_TYPES || types.indexOf(mime) > -1)) return true;
  2140.  
  2141.       if (!/\..*\.|:\//.test(site)) break;
  2142.       s = site.replace(/.*?(?::\/+|\.)/, '');
  2143.       if (s == site) break;
  2144.       site = s;
  2145.     } 
  2146.   },
  2147.   allowObject: function(url, mime) {
  2148.     url = this.objectKey(url);
  2149.     if (url in this.objectWhitelist) {
  2150.       var types = this.objectWhitelist[url];
  2151.       if(mime == "*") {
  2152.         if(types == this.ALL_TYPES) return;
  2153.         types = this.ALL_TYPES;
  2154.       } else {
  2155.         if(types.indexOf(mime) > -1) return;
  2156.         types.push(mime);
  2157.       }
  2158.     } else {
  2159.       this.objectWhitelist[url] = mime == "*" ? this.ALL_TYPES : [mime];
  2160.     }
  2161.     this.objectWhitelistLen++;
  2162.   },
  2163.   isAllowedObjectById: function(id, objectURL, parentURL, mime, site) {
  2164.     var url = this.getObjectURLWithId(id, objectURL, parentURL);
  2165.     return url && this.isAllowedObject(url, mime, site);
  2166.   },
  2167.   allowObjectById: function(id, objectURL, parentURL, mime) {
  2168.     var url = this.getObjectURLWithId(id, objectURL, parentURL);
  2169.     if (url) this.allowObject(url, mime);
  2170.   },
  2171.   getObjectURLWithId: function(id, objectURL, parentURL) {
  2172.     return id && objectURL.replace(/[\?#].*/, '') + "#!#" + id + "@" + encodeURIComponent(parentURL);
  2173.   },
  2174.   resetAllowedObjects: function() {
  2175.     this.objectWhitelist = {};
  2176.     this.objectWhitelistLen = 0;
  2177.   },
  2178.   
  2179.   
  2180.   countObject: function(embed, site) {
  2181.  
  2182.     if(!site) return;
  2183.     var doc = embed.ownerDocument;
  2184.     
  2185.     if (doc) {
  2186.       var topDoc = doc.defaultView.top.document;
  2187.       var os = this.getExpando(topDoc, "objectSites");
  2188.       if(os) {
  2189.         if(os.indexOf(site) < 0) os.push(site);
  2190.       } else {
  2191.         this.setExpando(topDoc, "objectSites", [site]);
  2192.       }
  2193.     
  2194.       this.opaqueIfNeeded(embed, doc);
  2195.     }
  2196.   },
  2197.   
  2198.   getPluginExtras: function(obj) {
  2199.     return this.getExpando(obj, "pluginExtras");
  2200.   },
  2201.   setPluginExtras: function(obj, extras) {
  2202.     this.setExpando(obj, "pluginExtras", extras);
  2203.     if (this.consoleDump & LOG_CONTENT_BLOCK) this.dump("Setting plugin extras on " + obj + " -> " + (this.getPluginExtras(obj) == extras)
  2204.       + ", " + (extras && extras.toSource())  );
  2205.     return extras;
  2206.   },
  2207.   
  2208.   getExpando: function(domObject, key, defValue) {
  2209.     return domObject && domObject.__noscriptStorage && domObject.__noscriptStorage[key] || 
  2210.            (defValue ? this.setExpando(domObject, key, defValue) : null);
  2211.   },
  2212.   setExpando: function(domObject, key, value) {
  2213.     if (!domObject) return null;
  2214.     if (!domObject.__noscriptStorage) domObject.__noscriptStorage = {};
  2215.     if (domObject.__noscriptStorage) domObject.__noscriptStorage[key] = value;
  2216.     else if(this.consoleDump) this.dump("Warning: cannot set expando " + key + " to value " + value);
  2217.     return value;
  2218.   },
  2219.   
  2220.   cleanupBrowser: function(browser) {
  2221.     delete browser.__noscriptStorage;
  2222.   },
  2223.   
  2224.   hasVisibleLinks: function(document) {
  2225.     var links = document.links;
  2226.     var style, position;
  2227.     for (var j = 0, l; (l = links[j]); j++) {
  2228.       if (l && l.href && /^https?/i.test(l.href)) {
  2229.         if(l.offsetWidth || l.offsetHeight) return true;
  2230.         try {
  2231.           position = l.style.position;
  2232.           l.style.position = "absolute";
  2233.           if(l.offsetWidth || l.offsetHeight) return true;
  2234.         } finally {
  2235.           l.style.position = position;
  2236.         }
  2237.       }
  2238.     }
  2239.     return false;
  2240.   },
  2241.   detectJSRedirects: function(document) {
  2242.     if (this.jsredirectIgnore || this.jsEnabled) return 0;
  2243.     try {
  2244.       if (!/^https?:/.test(document.documentURI)) return 0;
  2245.       var hasVisibleLinks = this.hasVisibleLinks(document);
  2246.       if (!this.jsredirectForceShow && hasVisibleLinks ||
  2247.           this.isJSEnabled(this.getSite(document.documentURI))) 
  2248.         return 0;
  2249.       var j, len;
  2250.       var seen = [];
  2251.       var body = document.body;
  2252.       var cstyle = document.defaultView.getComputedStyle(body, "");
  2253.       if (cstyle) {
  2254.         if (cstyle.visibility != "visible") {
  2255.           body.style.visibility = "visible";
  2256.         }
  2257.         if (cstyle.display == "none") {
  2258.           body.style.display = "block";
  2259.         }
  2260.       }
  2261.       if (!hasVisibleLinks && document.links[0]) {
  2262.         var links = document.links;
  2263.         var l;
  2264.         for (j = 0, len = links.length; j < len; j++) {
  2265.           l = links[j];
  2266.           if (!(l.href && /^https?/.test(l.href))) continue;
  2267.           l = body.appendChild(l.cloneNode(true));
  2268.           l.style.visibility = "visible";
  2269.           l.style.display = "block";
  2270.           seen.push(l.href);
  2271.         }
  2272.       }
  2273.       
  2274.       var code, m, url, a;
  2275.       var container = null;
  2276.       var window;
  2277.       
  2278.       code = body && body.getAttribute("onload");
  2279.       const sources = code ? [code] : [];
  2280.       var scripts = document.getElementsByTagName("script");
  2281.       for (j = 0, len = scripts.length; j < len; j++) sources.push(scripts[j].innerHTML);
  2282.       scripts = null;
  2283.       
  2284.       if (!sources[0]) return 0;
  2285.       var follow = false;
  2286.       const findURL = /(?:(?:\b(?:open|replace)\s*\(|(?:\b(?:href|location|src|path|pathname|search)|(?:[Pp]ath|UR[IL]|[uU]r[il]))\s*=)\s*['"]|['"](?=https?:\/\/\w|\w*[\.\/\?]))([\?\/\.\w\-%\&][^\s'"]*)/g;
  2287.       const MAX_TIME = 1000;
  2288.       const MAX_LINKS = 30;
  2289.       const ts = Date.now();
  2290.       outerLoop:
  2291.       for (j = 0, len = sources.length; j < len; j++) {
  2292.         findURL.lastIndex = 0;
  2293.         code = sources[j];
  2294.         while ((m = findURL.exec(code))) {
  2295.           
  2296.           if (!container) {
  2297.             container = document.createElementNS(HTML_NS, "div");
  2298.             with (container.style) {
  2299.               backgroundImage = 'url("' + this.pluginPlaceholder + '")';
  2300.               backgroundRepeat = "no-repeat";
  2301.               backgroundPosition = "2px 2px";
  2302.               padding = "4px 4px 4px 40px";
  2303.               display = "block";
  2304.               minHeight = "32px";
  2305.               textAlign = "left";
  2306.             }
  2307.             window = document.defaultView;
  2308.             follow = this.jsredirectFollow && window == window.top &&  
  2309.               !window.frames[0] &&
  2310.               !document.evaluate('//body[normalize-space()!=""]', document, null, 
  2311.                 CI.nsIDOMXPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  2312.             document.body.appendChild(container);
  2313.           }
  2314.           url = m[1];
  2315.           a = document.createElementNS(HTML_NS, "a");
  2316.           a.href = url;
  2317.           container.appendChild(a);
  2318.           if (a.href.toLowerCase().indexOf("http") != 0 || seen.indexOf(a.href) > -1) {
  2319.              container.removeChild(a);
  2320.              continue;
  2321.           }
  2322.           seen.push(a.href);
  2323.           a.appendChild(document.createTextNode(a.href));
  2324.           container.appendChild(document.createElementNS(HTML_NS, "br"));
  2325.           
  2326.           if (seen.length >= MAX_LINKS || Date.now() - ts > MAX_TIME) break outerLoop;
  2327.         }
  2328.         
  2329.         if (follow && seen.length == 1) {
  2330.           this.log("[NoScript Following JS Redirection]: " + seen[0] + " FROM " + document.location.href); 
  2331.           
  2332.           this.doFollowMetaRefresh({
  2333.             uri: seen[0],
  2334.             document: document
  2335.           });  
  2336.         }
  2337.         
  2338.         if (Date.now() - ts > MAX_TIME) break;
  2339.       }
  2340.       return seen.length;
  2341.     } catch(e) { 
  2342.       this.dump(e.message + " while processing JS redirects");
  2343.       return 0; 
  2344.     }
  2345.   }
  2346. ,
  2347.   processScriptElements: function(document, sites) {
  2348.     var scripts = document.getElementsByTagName("script");
  2349.     var scount = scripts.length;
  2350.     if (scount) {
  2351.       const HTMLElement = CI.nsIDOMHTMLElement;
  2352.       sites.scriptCount += scount;
  2353.       var script, scriptSrc;
  2354.       var nselForce = this.nselForce && sites.length && this.isJSEnabled(sites[sites.length - 1]);
  2355.       var isHTMLScript;
  2356.       while (scount-- > 0) {
  2357.         script = scripts.item(scount);
  2358.         isHTMLScript = script instanceof HTMLElement;
  2359.         if (isHTMLScript) {
  2360.           scriptSrc = script.src;
  2361.         } else if(script) {
  2362.           scriptSrc = script.getAttribute("src");
  2363.           if (!/^[a-z]+:\/\//i.test(scriptSrc)) continue;
  2364.         } else continue;
  2365.         
  2366.         scriptSrc = this.getSite(scriptSrc);
  2367.         if (scriptSrc) {
  2368.           sites.push(scriptSrc);
  2369.           if (nselForce && isHTMLScript && !this.isJSEnabled(scriptSrc)) {
  2370.             this.showNextNoscriptElement(script);
  2371.           }
  2372.         }
  2373.       }
  2374.     }
  2375.   },
  2376.   
  2377.   showNextNoscriptElement: function(script, doc) { 
  2378.     const HTMLElement = CI.nsIDOMHTMLElement;
  2379.     var child, el, ss, j;
  2380.     for (var node = script; node = node.nextSibling;) {
  2381.       try {
  2382.         if (node instanceof HTMLElement) {
  2383.           if (node.tagName.toUpperCase() != "NOSCRIPT") return;
  2384.           if (node.getAttribute("class") == "noscript-show") return;
  2385.           node.setAttribute("class", "noscript-show");
  2386.           child = node.firstChild;
  2387.           if (child.nodeType != 3) return;
  2388.           el = node.ownerDocument.createElementNS(HTML_NS, "span");
  2389.           el.className = "noscript-show";
  2390.           el.innerHTML = child.nodeValue;
  2391.           // remove noscript script children, see evite.com bug 
  2392.           ss = el.getElementsByTagName("script");
  2393.           for(j = ss.length; j-- > 0;) el.removeChild(ss[j]);
  2394.           node.replaceChild(el, child);
  2395.         }
  2396.       } catch(e) {
  2397.         this.dump(e.message + " while showing NOSCRIPT element");
  2398.       }
  2399.     }
  2400.   },
  2401.   
  2402.   metaRefreshWhitelist: {},
  2403.   processMetaRefresh: function(document, notifyCallback) {
  2404.     var docShell = DOM.getDocShellForWindow(document.defaultView);
  2405.     if (!this.forbidMetaRefresh ||    
  2406.        this.metaRefreshWhitelist[document.documentURI] ||
  2407.        this.isJSEnabled(this.getSite(document.documentURI)) ||
  2408.        !document.getElementsByTagName("noscript")[0]
  2409.        ) {
  2410.       if (!docShell.allowMetaRedirects) this.disableMetaRefresh(docShell); // refresh blocker courtesy
  2411.       return;
  2412.     }
  2413.     try {
  2414.       var refresh, content, timeout, uri;
  2415.       var rr = document.getElementsByTagName("meta");
  2416.       for (var j = 0; (refresh = rr[j]); j++) {
  2417.         if (!/refresh/i.test(refresh.httpEquiv)) continue;
  2418.         content = refresh.content.split(/[,;]/, 2);
  2419.         uri = content[1];
  2420.         if (uri) {
  2421.           if (notifyCallback && !(document.documentURI in this.metaRefreshWhitelist)) {
  2422.             timeout = content[0];
  2423.             uri = uri.replace (/^\s*/, "").replace (/^URL/i, "URL").split("URL=", 2)[1];
  2424.             try {
  2425.               notifyCallback({ 
  2426.                 docShell: docShell,
  2427.                 document: document,
  2428.                 baseURI: docShell.currentURI,
  2429.                 uri: uri, 
  2430.                 timeout: timeout
  2431.               });
  2432.             } catch(e) {
  2433.               dump("[NoScript]: " + e + " notifying meta refresh at " + document.documentURI + "\n");
  2434.             }
  2435.           }
  2436.           document.defaultView.addEventListener("pagehide", function(ev) {
  2437.               ev.currentTarget.removeEventListener(ev.type, arguments.callee, false);
  2438.               docShell.allowMetaRedirects = true;
  2439.               document = docShell = null;
  2440.           }, false);
  2441.           this.disableMetaRefresh(docShell);
  2442.           return;
  2443.         }
  2444.       }
  2445.     } catch(e) {
  2446.       dump("[NoScript]: " + e + " processing meta refresh at " + document.documentURI + "\n");
  2447.     }
  2448.   },
  2449.   doFollowMetaRefresh: function(metaRefreshInfo, forceRemember) {
  2450.     var document = metaRefreshInfo.document;
  2451.     if (forceRemember || this.getPref("forbidMetaRefresh.remember", false)) {
  2452.       this.metaRefreshWhitelist[document.documentURI] = metaRefreshInfo.uri;
  2453.     }
  2454.     var docShell = metaRefreshInfo.docShell || DOM.getDocShellForWindow(document.defaultView); 
  2455.     this.enableMetaRefresh(docShell);
  2456.     if (docShell instanceof CI.nsIRefreshURI) {
  2457.       var baseURI = metaRefreshInfo.baseURI || IOS.newURI(document.documentURI, null, null);
  2458.       docShell.setupRefreshURIFromHeader(baseURI, "0;" + metaRefreshInfo.uri);
  2459.     }
  2460.   },
  2461.   doBlockMetaRefresh: function(metaRefreshInfo) {
  2462.     if (this.getPref("forbidMetaRefresh.remember", true)) {
  2463.       var document = metaRefreshInfo.document;
  2464.       this.metaRefreshWhitelist[document.documentURI] = null;
  2465.     }
  2466.   },
  2467.   
  2468.   enableMetaRefresh: function(docShell) {
  2469.     if (docShell) {
  2470.       docShell.allowMetaRedirects = true;
  2471.       docShell.resumeRefreshURIs();
  2472.       // if(this.consoleDump) dump("Enabled META refresh on " + (docShell.currentURI && docShell.currentURI.spec) + "\n");
  2473.     }
  2474.   },
  2475.   disableMetaRefresh: function(docShell) {
  2476.     if (docShell) {
  2477.       docShell.suspendRefreshURIs();
  2478.       docShell.allowMetaRedirects = false;
  2479.       if (docShell instanceof CI.nsIRefreshURI) {
  2480.         docShell.cancelRefreshURITimers();
  2481.       }
  2482.       // if(this.consoleDump) dump("Disabled META refresh on " + (docShell.currentURI && docShell.currentURI.spec) + "\n");
  2483.     }
  2484.   },
  2485.   
  2486.   
  2487.   
  2488.   // These catch both Paypal's variant,
  2489.   // if (parent.frames.length > 0){ top.location.replace(document.location); }
  2490.   // and the general concise idiom with its common reasonable permutations,
  2491.   // if (self != top) top.location = location
  2492.   _frameBreakNoCapture: /\bif\s*\(\s*(?:(?:(?:window|self|top)\s*\.\s*)*(?:window|self|top)\s*!==?\s*(?:(?:window|self|top)\s*\.\s*)*(?:window|self|top)|(?:(?:window|self|parent|top)\s*\.\s*)*(?:parent|top)\.frames\.length\s*(?:!==?|>)\s*0)\s*\)\s*\{?\s*(?:window\s*\.\s*)?top\s*\.\s*location\s*(?:\.\s*(?:replace|assign)\s*\(|(?:\s*\.\s*href\s*)?=)\s*(?:(?:document|window|self)\s*\.\s*)?location(?:\s*.\s*href)?\s*\)?\s*;?\s*\}?/,
  2493.   _frameBreakCapture: /^\(function\s[^\{]+\{\s*if\s*\(\s*(?:(?:(?:window|self|top)\s*\.\s*)*(window|self|top)\s*!==?\s*(?:(?:window|self|top)\s*\.\s*)*(window|self|top)|(?:(?:window|self|parent|top)\s*\.\s*)*(?:parent|top)\.frames\.length\s*(?:!==?|>)\s*0)\s*\)\s*\{?\s*(?:window\s*\.\s*)?top\s*\.\s*location\s*(?:\.\s*(?:replace|assign)\s*\(|(?:\s*\.\s*href\s*)?=)\s*(?:(?:document|window|self)\s*\.\s*)?location(?:\s*.\s*href)?\s*\)?\s*;?\s*\}?/,
  2494.   doEmulateFrameBreak: function(w) {
  2495.     // If JS is disabled we check the top 5 script elements of the page searching for the first inline one:
  2496.     // if it starts with a frame breaker, we honor it.
  2497.     var d = w.document;
  2498.     var url = d.URL;
  2499.     if (!/^https?:/.test(url) || this.isJSEnabled(this.getSite(url))) return false;
  2500.     var ss = d.getElementsByTagName("script");
  2501.     var sc, m, code;
  2502.     for (var j = 0, len = 5, s; j < len && (s = ss[j]); j++) {
  2503.       code = s.innerHTML;
  2504.       if (code && /\S/.test(code)) {
  2505.         if (this._frameBreakNoCapture.test(code)) {
  2506.           try {
  2507.             sc = sc || new SyntaxChecker();
  2508.             var m;
  2509.             if (sc.check(code) && 
  2510.                 (m = sc.lastFunction.toSource().match(this._frameBreakCapture)) && 
  2511.                 (!m[1] || (m[1] == "top" || m[2] == "top") && m[1] != m[2])) {
  2512.               var top = w.top;
  2513.  
  2514.               var ds = DOM.getDocShellForWindow(top);
  2515.               var allowJavascript = ds.allowJavascript;
  2516.               var allowPlugins = ds.allowPlugins;
  2517.               if (allowJS) { // temporarily disable JS & plugins on the top frame to prevent counter-busting 
  2518.                 ds.allowJavascript = ds.allowPlugins = false;
  2519.                 top.addEventListener("pagehide", function(ev) {
  2520.                   ev.currentTarget.removeEventListener(ev.type, arguments.calle, false);
  2521.                   ds.allowJavascript = allowJavascript;
  2522.                   ds.allowPlugins = allowPlugins;
  2523.                 }, false);
  2524.               }
  2525.               top.location.href = url;
  2526.               var body = d.body;
  2527.               if (body) while(body.firstChild) body.removeChild(body.firstChild);
  2528.               return true;
  2529.             }
  2530.           } catch(e) {
  2531.             this.dump("Error checking " + code + ": " + e.message);
  2532.           }
  2533.         }
  2534.         break; // we want to check the first inline script only
  2535.       }
  2536.     }
  2537.     return false;
  2538.   },
  2539.   
  2540.   knownFrames: {
  2541.     _history: {},
  2542.     add: function(url, parentSite) {
  2543.       var f = this._history[url] || (this._history[url] = []);
  2544.       if (f.indexOf(parentSite) > -1) return;
  2545.       f.push(parentSite);
  2546.     },
  2547.     isKnown: function(url, parentSite) {
  2548.       var f = this._history[url];
  2549.       return f && f.indexOf(parentSite) > -1;
  2550.     },
  2551.     reset: function() {
  2552.       this._history = {}
  2553.     }
  2554.   },
  2555.   
  2556.   frameContentLoaded: function(w) {
  2557.     if (this.emulateFrameBreak && this.doEmulateFrameBreak(w)) return; // we're no more framed
  2558.  
  2559.     if ((this.forbidIFrames && w.frameElement instanceof CI.nsIDOMHTMLIFrameElement ||
  2560.          this.forbidFrames  && w.frameElement instanceof CI.nsIDOMHTMLFrameElement) &&
  2561.         this.getPref("rememberFrames", false)) {
  2562.       this.knownFrames.add(w.location.href, this.getSite(w.parent.location.href));
  2563.     }
  2564.   },
  2565.   
  2566.   
  2567.   handleBookmark: function(url, openCallback) {
  2568.     if (!url) return true;
  2569.     const allowBookmarklets = !this.getPref("forbidBookmarklets", false);
  2570.     const allowBookmarks = this.getPref("allowBookmarks", false);
  2571.     if (!this.jsEnabled && 
  2572.       (allowBookmarks || allowBookmarklets)) {
  2573.       try {
  2574.         var site;
  2575.         if (allowBookmarklets && /^\s*(?:javascript|data):/i.test(url)) {
  2576.           var ret = this.executeJSURL(url, openCallback);
  2577.         } else if (allowBookmarks && !(this.isJSEnabled(site = this.getSite(url)) || this.isUntrusted(site))) {
  2578.           this.setJSEnabled(site, true);
  2579.           this.savePrefs();
  2580.         }
  2581.         return ret;
  2582.       } catch(e) {
  2583.         if (ns.consoleDump) ns.dump(e + " " + e.stack);
  2584.       }
  2585.     }
  2586.     return false;
  2587.   },
  2588.   
  2589.   executeJSURL: function(url, openCallback) {
  2590.     var browserWindow = DOM.mostRecentBrowserWindow;
  2591.     var browser = browserWindow.noscriptOverlay.currentBrowser;
  2592.     if(!browser) return false;
  2593.     
  2594.     var window = browser.contentWindow;
  2595.     if(!window) return false;
  2596.     
  2597.     var site = this.getSite(window.document.documentURI) || this.getExpando(browser, "jsSite");
  2598.     if (!this.jsEnabled) {
  2599.       if(this.consoleDump) this.dump("Executing JS URL " + url + " on site " + site);
  2600.       
  2601.       var snapshots = {
  2602.         docJS: browser.webNavigation.allowJavascript,
  2603.         siteJS: this.isJSEnabled(site)
  2604.       };
  2605.     
  2606.  
  2607.       try {
  2608.         this.setJSEnabled(site, true);
  2609.         browser.webNavigation.allowJavascript = true;
  2610.         this.jsEnabled = ns.getPref("allowBookmarkletImports");
  2611.           
  2612.      
  2613.         if (Thread.canSpin) { // async evaluation, after bug 351633 landing
  2614.           var doc = window.document;
  2615.           try {
  2616.             if (!(snapshots.siteJS && snapshots.docJS)) {
  2617.               this._runJS(window, "(" +
  2618.                 function() {
  2619.                   var tt = [];
  2620.                   window.setTimeout = window.setInterval = function(f, d, a) {
  2621.                     if (typeof(f) != 'function') f = new Function(f || '');
  2622.                     tt.push({f: f, d: d, a: a});
  2623.                     return 0;
  2624.                   };
  2625.                   window.__runTimeouts = function() {
  2626.                     var t, count = 0;
  2627.                     while (tt.length && count++ < 50) { // let's prevent infinite pseudo-loops
  2628.                       tt.sort(function(b, a) { return a.d < b.d ? -1 : (a.d > b.d ? 1 : 0); });
  2629.                       t = tt.pop();
  2630.                       t.f.call(window, t.a);
  2631.                     }
  2632.                     delete window.__runTimeouts;
  2633.                     delete window.setTimeout;
  2634.                   };
  2635.                 }.toSource()
  2636.               + ")()");
  2637.             }
  2638.             
  2639.             if (openCallback) {
  2640.               window.location.href = url;
  2641.             } else {
  2642.               var s = doc.createElement("script");
  2643.               s.appendChild(doc.createTextNode(url));
  2644.               doc.documentElement.appendChild(s);
  2645.             }
  2646.             
  2647.             Thread.yieldAll();
  2648.             if (!(snapshots.siteJS && snapshots.docJS)) {
  2649.               this._runJS(window,
  2650.                         "if (typeof window.__runTimeouts == 'function') window.__runTimeouts()");
  2651.             }
  2652.             
  2653.           } catch(e) {
  2654.             if(this.consoleDump) this.dump("JS URL execution failed: " + e);
  2655.           }
  2656.         } else {
  2657.           openCallback(url);
  2658.         }
  2659.         return true;
  2660.       } finally {
  2661.         this.jsEnabled = false;
  2662.         this.setJSEnabled(site, snapshots.siteJS);
  2663.         this.setExpando(browser, "jsSite", site);
  2664.         if (!browser.webNavigation.isLoadingDocument && this.getSite(browser.webNavigation.currentURI.spec) == site)
  2665.           browser.webNavigation.allowJavascript = snapshots.docJS;
  2666.         if (this.consoleDump & LOG_JS)
  2667.           this.dump("Restored snapshot permissions on " + site + "/" + (browser.webNavigation.isLoadingDocument ? "loading" : browser.webNavigation.currentURI.spec));
  2668.       }
  2669.     }
  2670.     
  2671.     return false;
  2672.   },
  2673.   _runJS: function(window, s) {
  2674.     window.location.href = "javascript:" + encodeURIComponent(s + "; void(0);");
  2675.     Thread.yieldAll();
  2676.   },
  2677.   
  2678.   isCheckedChannel: function(c) {
  2679.     return IOUtil.extractFromChannel(c, "noscript.checkedChannel", true);
  2680.   },
  2681.   setCheckedChannel: function(c, v) {
  2682.     IOUtil.attachToChannel(c, "noscript.checkedChannel", v ? {} : null);
  2683.   },
  2684.   
  2685.   createCheckedXHR: function(method, url, async) {
  2686.     if (typeof(async) == "undefined") async = true;
  2687.     var xhr = CC["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(CI.nsIXMLHttpRequest);
  2688.     xhr.open(method, url, async);
  2689.     this.setCheckedChannel(xhr.channel, true);
  2690.     return xhr;
  2691.   },
  2692.   
  2693.   mimeEssentials: function(mime) {
  2694.      return mime && mime.replace(/^application\/(?:x-)?/, "") || "";
  2695.   },
  2696.   urlEssentials: function(s) {
  2697.     // remove query, hash and intermediate path
  2698.     return s.replace(/[#\?].*/g, '').replace(/(.*?\w\/).+?(\/[^\/]+)$/, '$1...$2');
  2699.   },
  2700.   cssMimeIcon: function(mime, size) {
  2701.     return mime == "application/x-shockwave-flash"
  2702.     ? // work around for Windows not associating a sane icon to Flash
  2703.       'url("' + this.skinBase + "flash" + size + '.png")'
  2704.     : /^application\/x-java\b/i.test(mime)
  2705.       ? 'url("' + this.skinBase + "java" + size + '.png")'
  2706.       : mime == "application/x-silverlight"
  2707.         ? 'url("' + this.skinBase + "somelight" + size + '.png")'
  2708.         : /^font\b/i.test(mime)
  2709.           ? 'url("' + this.skinBase + 'font.png")'
  2710.           : 'url("moz-icon://noscript?size=' + size + '&contentType=' + mime.replace(/[^\w-\/]/g, '') + '")';
  2711.   },
  2712.   
  2713.   
  2714.   findObjectAncestor: function(embed) {
  2715.     const objType = CI.nsIDOMHTMLObjectElement;
  2716.     if (embed instanceof CI.nsIDOMHTMLEmbedElement || embed instanceof objType) {
  2717.       for (var o = embed; (o = o.parentNode);) {
  2718.         if (o instanceof objType) return o;
  2719.       }
  2720.     }
  2721.     return embed;
  2722.   },
  2723.   
  2724.   findPluginExtras: function(document) {
  2725.     var res = this.getExpando(document, "pluginExtras", []);
  2726.     if ("opaqueHere" in res) return res;
  2727.     var url = document.defaultView.location.href;
  2728.     res.opaqueHere = this.appliesHere(this.opaqueObject, url) && /^(?:ht|f)tps?:/i.test(url);
  2729.     return res;
  2730.   },
  2731.   
  2732.   
  2733.   OpaqueHandlers: {
  2734.     
  2735.     getOpaqued: function(w) {
  2736.       const cs = "__noscriptOpaqued__";
  2737.       if (w.__noscriptOpaquedObjects) return w.__noscriptOpaquedObjects;
  2738.       var doc = w.document;
  2739.       if (doc.getElementsByClassName) return Array.slice(doc.getElementsByClassName(cs));
  2740.       var results = [];
  2741.       var query = doc.evaluate(
  2742.           "//*[contains(concat(' ', @class, ' '), ' " + cs + " ')]",
  2743.           w.document, null, CI.nsIDOMXPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  2744.       for (var i = 0, len = query.snapshotLength; i < len; i++) {
  2745.          results.push(query.snapshotItem(i));
  2746.       }
  2747.       return results;                                                     
  2748.     },
  2749.     
  2750.     get fixScrollers() {
  2751.       var self = this;
  2752.       var f = function(ev) {
  2753.         var w = ev.currentTarget;
  2754.         
  2755.         var o, s, cs;
  2756.         var oo = self.getOpaqued(w);
  2757.         var d = w.document;
  2758.         var de = d.documentElement;
  2759.         var db = d.body;
  2760.         var scrollers = [];
  2761.         for each(o in oo) {
  2762.           if (o == de || o == db) continue;
  2763.           try {
  2764.             s = w.getComputedStyle(o, '');
  2765.             if (s && s.display == "block" && s.overflow == "hidden" || /^(?:i?frame|object)$/i.test(o.tagName))
  2766.               scrollers.push(o);
  2767.           } catch(e) {
  2768.             dump(e + ", " + e.stack + "\n");
  2769.           }
  2770.         }
  2771.         for each(var o in scrollers) {
  2772.           DOM.addClass(o, "__noscriptScrolling__");
  2773.         }
  2774.         
  2775.         switch(ev.type) {
  2776.           case "timeout":
  2777.             Thread.delay(arguments.callee, ev.timeout, this, [ev]);
  2778.             break;
  2779.           case "load":
  2780.             w.__noscriptOpaquedObjects = null;
  2781.           default:
  2782.             w.removeEventListener(ev.type, arguments.callee, false);
  2783.         }
  2784.       };
  2785.       delete this.fixScrollers;
  2786.       return this.fixScrollers = f;
  2787.     },
  2788.     
  2789.     scheduleFixScrollers: function(w, timeout) {
  2790.       var ev = { currentTarget: w, type: "timeout", timeout: timeout };
  2791.       Thread.delay(this.fixScrollers, timeout, this, [ev]);
  2792.     },
  2793.     
  2794.     getOpaquedObjects: function(w, noscroll) {
  2795.       var oo = w.__noscriptOpaquedObjects;
  2796.       if (!oo) {
  2797.         oo = [];
  2798.         w.__noscriptOpaquedObjects = oo;
  2799.         if (!noscroll) {
  2800.           w.addEventListener("load", this.fixScrollers, false);
  2801.           w.addEventListener("DOMContentLoaded", this.fixScrollers, false);
  2802.           this.scheduleFixScrollers(w, 3000);
  2803.         }
  2804.       }
  2805.       return oo;
  2806.     }
  2807.     
  2808.   },
  2809.   
  2810.   opaque: function(o, scrollNow) {
  2811.     if (o.__noscriptOpaqued) return;
  2812.     try {
  2813.       
  2814.       if (o.contentDocument && this.clearClickHandler.sameSiteParents(o.contentDocument.defaultView)) return;
  2815.       
  2816.       var d = o.ownerDocument;
  2817.       var w = d.defaultView;
  2818.       var oh = this.OpaqueHandlers;
  2819.       var oo = oh.getOpaquedObjects(w, scrollNow || this.clearClickHandler.isSupported(d) && this.appliesHere(this.clearClick, w.top.location.href));
  2820.       if (scrollNow) oh.scheduleFixScrollers(w, 1);
  2821.       do {
  2822.         o.__noscriptOpaqued = true;
  2823.         o.style.opacity = "";
  2824.         DOM.addClass(o, "__noscriptOpaqued__");
  2825.         oo.push(o);
  2826.         if (this.consoleDump & LOG_CLEARCLICK) this.dump("Opacizing " + o.tagName);
  2827.         o = o.parentNode;
  2828.       } while(o && o.style && !o.__noscriptOpaqued);
  2829.     } catch(e) {
  2830.       this.dump("Error opacizing " + o.tagName + ": " + e.message + ", " + e.stack);
  2831.     }
  2832.   },
  2833.   
  2834.   opaqueIfNeeded: function(o, doc) {
  2835.     if (this.findPluginExtras(doc || o.ownerDocument).opaqueHere)
  2836.       this.opaque(o);
  2837.   },
  2838.   
  2839.   appliesHere: function(pref, url) {
  2840.     return pref && ((ANYWHERE & pref) == ANYWHERE||
  2841.        (this.isJSEnabled(this.getSite(url))
  2842.         ? (WHERE_TRUSTED & pref) : (WHERE_UNTRUSTED & pref)
  2843.        )
  2844.       );
  2845.   },
  2846.   
  2847.   _preprocessObjectInfo: function(doc) {
  2848.     var pe = this.getExpando(doc, "pe");
  2849.     if (!pe) return null;
  2850.     this.setExpando(doc, "pe", null);
  2851.     var ret = [], o;
  2852.     for (var j = pe.length; j-- > 0;) {
  2853.       o = pe[j];
  2854.       try {
  2855.         if (this.getExpando(o, "silverlight")) {
  2856.           o.embed = this._attachSilverlightExtras(o.embed, o.pluginExtras);
  2857.           if (!o.embed) continue; // skip unconditionally to prevent in-page Silverlight placeholders
  2858.         }
  2859.         o.originalEmbed = o.embed;
  2860.         this.setPluginExtras(o.embed = this.findObjectAncestor(o.embed), o.pluginExtras);
  2861.         if (o.embed.ownerDocument) ret.push(o);
  2862.        } catch(e1) { 
  2863.          if(this.consoleDump & LOG_CONTENT_BLOCK) 
  2864.            this.dump("Error setting plugin extras: " + 
  2865.              (o && o.pluginExtras && o.pluginExtras.url) + ", " + e1); 
  2866.        }
  2867.     }
  2868.     return ret;
  2869.   },
  2870.   
  2871.   processObjectElements: function(document, sites, loaded) {
  2872.     var pluginExtras = this.findPluginExtras(document);
  2873.     sites.pluginCount += pluginExtras.length;
  2874.     sites.pluginExtras.push(pluginExtras);
  2875.     
  2876.     var objInfo = this._preprocessObjectInfo(document);
  2877.     if (!objInfo) return;
  2878.     var count = objInfo.length;
  2879.     sites.pluginCount += count;
  2880.     
  2881.     
  2882.     var collapse = this.collapseObject;
  2883.  
  2884.     var oi, object, objectTag;
  2885.     var anchor, innerDiv, iconSize;
  2886.     var extras;
  2887.     var cssLen, cssCount, cssProp, cssDef;
  2888.     var style;
  2889.     
  2890.     var replacements = null;
  2891.     var w, h;
  2892.     var opaque = pluginExtras.opaqueHere;
  2893.     
  2894.     var minSize = this.getPref("placeholderMinSize");
  2895.     var restrictedSize;
  2896.     
  2897.     var forcedCSS = ";";
  2898.     var pluginDocument = false;       
  2899.     try {
  2900.       pluginDocument = count == 1 && (objInfo[0].pluginExtras.url == document.URL) && !objInfo[0].embed.nextSibling;
  2901.       if (pluginDocument) {
  2902.         collapse = false;
  2903.         forcedCSS = ";outline-style: none !important;-moz-outline-style: none !important;";
  2904.       }
  2905.     } catch(e) {}
  2906.     
  2907.     var win = document.defaultView;      
  2908.  
  2909.     while (count--) {
  2910.       oi = objInfo[count];
  2911.       object = oi.embed;
  2912.       extras = oi.pluginExtras;
  2913.       objectTag = object.tagName;
  2914.       
  2915.       try {
  2916.  
  2917.         extras.site = this.getSite(extras.url);
  2918.         
  2919.         if(!this.showUntrustedPlaceholder && this.isUntrusted(extras.site)) {
  2920.           this.removeAbpTab(object);
  2921.           continue;
  2922.         }
  2923.         
  2924.         extras.tag = "<" + (this.isLegacyFrameReplacement(object) ? "FRAME" : objectTag.toUpperCase()) + ">";
  2925.         extras.title =  extras.tag + ", " +  
  2926.             this.mimeEssentials(extras.mime) + "@" + extras.url;
  2927.         
  2928.        if ((extras.alt = object.getAttribute("alt")))
  2929.           extras.title += ' "' + extras.alt + '"'
  2930.         
  2931.         
  2932.         anchor = document.createElementNS(HTML_NS, "a");
  2933.         anchor.id = object.id;
  2934.         anchor.href = extras.url;
  2935.         anchor.setAttribute("title", extras.title);
  2936.         
  2937.         this.setPluginExtras(anchor, extras);
  2938.         this.setExpando(anchor, "removedNode", object);
  2939.      
  2940.         (replacements = replacements || []).push({object: object, placeholder: anchor, extras: extras });
  2941.  
  2942.         if (this.showPlaceholder) {
  2943.           if (!pluginExtras.overlayListener) {
  2944.             pluginExtras.overlayListener = true;
  2945.             win.addEventListener("click", this.bind(this.onOverlayedPlaceholderClick), true);
  2946.           }
  2947.           anchor.addEventListener("click", this.bind(this.onPlaceholderClick), true);
  2948.           anchor.className = "__noscriptPlaceholder__";
  2949.           if (this.abpRemoveTabs) this.removeAbpTab(object);
  2950.         } else {
  2951.           anchor.className = "";
  2952.           if(collapse) anchor.style.display = "none";
  2953.           else anchor.style.visibility = "hidden";
  2954.           this.removeAbpTab(object); 
  2955.           continue;
  2956.         }
  2957.         
  2958.         innerDiv = document.createElementNS(HTML_NS, "div");
  2959.         innerDiv.className = "__noscriptPlaceholder__1";
  2960.         
  2961.         with (anchor.style) {
  2962.           padding = margin = borderWidth = "0px";
  2963.           outlineOffset = MozOutlineOffset = "-1px"; 
  2964.           display = "inline";
  2965.         }
  2966.         
  2967.         cssDef = "";
  2968.         style = win.getComputedStyle(oi.originalEmbed, null);
  2969.         if (style) {
  2970.           for (cssCount = 0, cssLen = style.length; cssCount < cssLen; cssCount++) {
  2971.             cssProp = style.item(cssCount);
  2972.             cssDef += cssProp + ": " + style.getPropertyValue(cssProp) + ";";
  2973.           }
  2974.           
  2975.           innerDiv.setAttribute("style", cssDef + forcedCSS);
  2976.           
  2977.           restrictedSize = (collapse || style.display === "none" || style.visibility === "hidden");
  2978.           
  2979.         } else restrictedSize = collapse;
  2980.         
  2981.         if (restrictedSize) {
  2982.           innerDiv.style.maxWidth = anchor.style.maxWidth = "32px";
  2983.           innerDiv.style.maxHeight = anchor.style.maxHeight = "32px";
  2984.         }
  2985.         
  2986.         innerDiv.style.visibility = "visible";
  2987.  
  2988.         anchor.appendChild(innerDiv);
  2989.         
  2990.         // icon div
  2991.         innerDiv = innerDiv.appendChild(document.createElementNS(HTML_NS, "div"));
  2992.         innerDiv.className = "__noscriptPlaceholder__2";
  2993.         
  2994.         if(restrictedSize || style && (parseInt(style.width) < 64 || parseInt(style.height) < 64)) {
  2995.           innerDiv.style.backgroundPosition = "bottom right";
  2996.           iconSize = 16;
  2997.           w = parseInt(style.width);
  2998.           h = parseInt(style.height);
  2999.           if (minSize > w || minSize > h) {
  3000.             with (innerDiv.parentNode.style) {
  3001.               minWidth = Math.max(w, Math.min(document.documentElement.clientWidth - object.offsetLeft, minSize)) + "px";
  3002.               minHeight = Math.max(h, Math.min(document.documentElement.clientHeight - object.offsetTop, minSize)) + "px";
  3003.             }
  3004.             with (anchor.style) {
  3005.               overflow = "visible";
  3006.               display = "block";
  3007.               width = w + "px";
  3008.               height = h + "px";
  3009.             }
  3010.             anchor.style.float = "left";
  3011.           }
  3012.         } else {
  3013.           iconSize = 32;
  3014.           innerDiv.style.backgroundPosition = "center";
  3015.         }
  3016.         innerDiv.style.backgroundImage = this.cssMimeIcon(extras.mime, iconSize);
  3017.         
  3018.       } catch(objectEx) {
  3019.         ns.dump(objectEx + " processing plugin " + count + "@" + document.documentURI + "\n");
  3020.       }
  3021.       
  3022.     }
  3023.  
  3024.     if (replacements) {
  3025.       this.delayExec(this.createPlaceholders, 0, replacements, pluginExtras, document);
  3026.     }
  3027.   },
  3028.   
  3029.   createPlaceholders: function(replacements, pluginExtras, document) {
  3030.     for each (var r in replacements) {
  3031.       try {
  3032.         if (r.extras.pluginDocument) {
  3033.           this.setPluginExtras(r.object, null);
  3034.           if (r.object.parentNode) r.object.parentNode.insertBefore(r.placeholder, r.object);
  3035.         } else {
  3036.           if (r.object.parentNode) r.object.parentNode.replaceChild(r.placeholder, r.object);
  3037.         }
  3038.         r.extras.placeholder = r.placeholder;
  3039.         this._collectPluginExtras(pluginExtras, r.extras);
  3040.         if (this.abpInstalled && !this.abpRemoveTabs)
  3041.           this.adjustAbpTab(r.placeholder);
  3042.       } catch(e) {
  3043.         this.dump(e);
  3044.       }
  3045.     }
  3046.     this.syncUI(document.defaultView.top);
  3047.   },
  3048.   
  3049.   bind: function(f) {
  3050.     return "_bound" in f ? f._bound : f._bound = (function() { return f.apply(ns, arguments); });
  3051.   },
  3052.   
  3053.   onPlaceholderClick: function(ev, anchor) {
  3054.     if (ev.button) return;
  3055.     anchor = anchor || ev.currentTarget
  3056.     const object = this.getExpando(anchor, "removedNode");
  3057.     
  3058.     if (object) try {
  3059.       if (ev.shiftKey) {
  3060.         anchor.style.display = "none";
  3061.         anchor.id = anchor.className = "";
  3062.         this.removeAbpTab(anchor);
  3063.         return;
  3064.       }
  3065.       this.checkAndEnablePlaceholder(anchor, object);
  3066.     } finally {
  3067.       ev.preventDefault();
  3068.       ev.stopPropagation();
  3069.     }
  3070.   },
  3071.   
  3072.   onOverlayedPlaceholderClick: function(ev) {
  3073.     var el = ev.originalTarget;
  3074.     var d = el.ownerDocument;
  3075.     var style = d.defaultView.getComputedStyle(el, "");
  3076.     if (style.position == "absolute") {
  3077.       var pluginExtras = this.findPluginExtras(d);
  3078.       if (!pluginExtras) return;
  3079.       var p = { x: ev.clientX, y: ev.clientY };
  3080.       for (var j = pluginExtras.length; j-- > 0;) {
  3081.         pe = pluginExtras[j];
  3082.         if (pe.placeholder) try {
  3083.           if (DOM.elementContainsPoint(pe.placeholder, p)) {
  3084.             var object = this.getExpando(pe.placeholder, "removedNode");
  3085.             if (object && !(object instanceof CI.nsIDOMHTMLAnchorElement))
  3086.               this.setExpando(object, "overlay", el);
  3087.             this.onPlaceholderClick(ev, pe.placeholder);
  3088.             return;
  3089.           }
  3090.         } catch(e) {
  3091.           if (ns.consoleDump) ns.dump(e);
  3092.         }
  3093.       }
  3094.     }
  3095.   },
  3096.   
  3097.   get abpInstalled() {
  3098.     delete this.abpInstalled;
  3099.     return this.abpInstalled = "@mozilla.org/adblockplus;1" in CC;
  3100.   },
  3101.   
  3102.   grabAbpTab: function(ref) {
  3103.     if (!this.abpInstalled) return null;
  3104.     var ot = ref.previousSibling;
  3105.     return (ot && (ot instanceof CI.nsIDOMHTMLAnchorElement) && !ot.firstChild && /\babp-objtab-/.test(ot.className))
  3106.       ? ot : null; 
  3107.   },
  3108.   
  3109.   removeAbpTab: function(ref) {
  3110.     var ot = this.grabAbpTab(ref);
  3111.     if (ot) {
  3112.       ot.parentNode.removeChild(ot);
  3113.     }
  3114.   },
  3115.   
  3116.   adjustAbpTab: function(ref) {
  3117.     var ot = this.grabAbpTab(ref);
  3118.     if (ot) {
  3119.       var style = ot.style;
  3120.       style.setProperty("left", ref.offsetWidth + "px", "important");
  3121.       if (!/\bontop\b/.test(ot.className)) {
  3122.         style.setProperty("top", ref.offsetHeight + "px", "important");
  3123.       }
  3124.     }
  3125.   },
  3126.   
  3127.   checkAndEnablePlaceholder: function(anchor, object) {
  3128.     if (!(object || (object = this.getExpando(anchor, "removedNode")))) {
  3129.       if (ns.consoleDump) ns.dump("Missing node on placeholder!");
  3130.       return;
  3131.     }
  3132.     
  3133.     if (ns.consoleDump) ns.dump("Enabling node from placeholder...");
  3134.     
  3135.     const extras = this.getPluginExtras(anchor);
  3136.     const browser = DOM.findBrowserForNode(anchor);
  3137.  
  3138.     if (!(extras && extras.url && extras.mime // && cache
  3139.       )) return;
  3140.    
  3141.     this.delayExec(this.checkAndEnableObject, 1,
  3142.       {
  3143.         browser: browser,
  3144.         window: browser.ownerDocument.defaultView,
  3145.         extras: extras,
  3146.         anchor: anchor,
  3147.         object: object
  3148.       });
  3149.   },
  3150.   
  3151.   confirmEnableObject: function(win, extras) {
  3152.     return extras.skipConfirmation || win.noscriptUtil.confirm(
  3153.       this.getAllowObjectMessage(extras.url, 
  3154.           (extras.tag || "<OBJECT>") + ", " + extras.mime), 
  3155.       "confirmUnblock"
  3156.     );
  3157.   },
  3158.   
  3159.   isLegacyFrameDocument: function(doc) {
  3160.     return (doc.defaultView.frameElement instanceof CI.nsIDOMHTMLFrameElement) && this.isPluginDocumentURL(doc.URL, "iframe");
  3161.   },
  3162.   isLegacyFrameReplacement: function(obj) {
  3163.      return (obj instanceof CI.nsIDOMHTMLIFrameElement || obj instanceof CI.nsIDOMHTMLAnchorElement) &&
  3164.            (obj.ownerDocument.defaultView.frameElement instanceof CI.nsIDOMHTMLFrameElement) &&
  3165.            obj.ownerDocument.URL == this.createPluginDocumentURL(obj.src || obj.href, "iframe");
  3166.   },
  3167.   
  3168.   checkAndEnableObject: function(ctx) {
  3169.     var extras = ctx.extras;
  3170.     if (this.confirmEnableObject(ctx.window, extras)) {
  3171.  
  3172.       var mime = extras.mime;
  3173.       var url = extras.url;
  3174.       
  3175.       this.allowObject(url, mime);
  3176.       var doc = ctx.anchor.ownerDocument;
  3177.       
  3178.       var isLegacyFrame = this.isLegacyFrameReplacement(ctx.object);
  3179.        
  3180.       if (isLegacyFrame || (mime == doc.contentType && 
  3181.           (ctx.anchor == doc.body.firstChild && 
  3182.            ctx.anchor == doc.body.lastChild ||
  3183.            (ctx.object instanceof CI.nsIDOMHTMLEmbedElement) && ctx.object.src != url))
  3184.         ) { // stand-alone plugin or frame
  3185.           doc.body.removeChild(ctx.anchor); // TODO: add a throbber
  3186.           if (isLegacyFrame) {
  3187.             this.setExpando(doc.defaultView.frameElement, "allowed", true);
  3188.             // doc.defaultView.frameElement.src = url;
  3189.             doc.defaultView.location.replace(url);
  3190.           } else this.quickReload(doc.defaultView, true);
  3191.       } else if (this.requireReloadRegExp && this.requireReloadRegExp.test(mime) || this.getExpando(ctx, "requiresReload")) {
  3192.         this.quickReload(doc.defaultView);
  3193.       } else if (this.getExpando(ctx, "silverlight")) {
  3194.         this.allowObject(doc.documentURI, mime);
  3195.         this.quickReload(doc.defaultView);
  3196.       } else {
  3197.         this.setExpando(ctx.anchor, "removedNode", null);
  3198.         extras.placeholder = null;
  3199.         this.delayExec(function() {
  3200.           try {
  3201.             this.removeAbpTab(ctx.anchor);
  3202.             var jsEnabled = ns.isJSEnabled(ns.getSite(doc.documentURI));
  3203.             var obj = ctx.object.cloneNode(true);
  3204.             
  3205.             function reload() {
  3206.               ns.allowObjectById(obj.id, url, doc.documentURI, mime);
  3207.               ns.quickReload(doc.defaultView);
  3208.             }
  3209.             
  3210.             var isMedia = ("nsIDOMHTMLVideoElement" in CI) && (obj instanceof CI.nsIDOMHTMLVideoElement || obj instanceof CI.nsIDOMHTMLAudioElement);
  3211.             
  3212.             if (isMedia) {
  3213.               if (jsEnabled && !obj.controls) {
  3214.                 // we must reload, since the author-provided UI likely had no chance to wire events
  3215.                 reload();
  3216.                 return;
  3217.               }
  3218.               obj.autoplay = true;
  3219.             }
  3220.             
  3221.             
  3222.             this.setExpando(obj, "allowed", true);
  3223.             ctx.anchor.parentNode.replaceChild(obj, ctx.anchor);
  3224.             var style = doc.defaultView.getComputedStyle(obj, '');
  3225.             if (jsEnabled && ((obj.offsetWidth || parseInt(style.width)) < 2 || (obj.offsetHeight || parseInt(style.height)) < 2))
  3226.               Thread.delay(function() {
  3227.                 if (obj.offsetWidth < 2 || obj.offsetHeight < 2) reload();
  3228.               }, 100); // warning, asap() or timeout=0 won't always work!
  3229.             
  3230.             
  3231.           } finally {
  3232.             ctx = null;
  3233.           }
  3234.         }, 10);
  3235.         return;
  3236.       }
  3237.     }
  3238.     ctx = null;
  3239.   },
  3240.  
  3241.   getSites: function(browser) {
  3242.     var sites = [];
  3243.     sites.scriptCount = 0;
  3244.     sites.pluginCount = 0;
  3245.     sites.pluginExtras = [];
  3246.     sites.pluginSites = [];
  3247.     sites.docSites = [];
  3248.     try {
  3249.       sites = this._enumerateSites(browser, sites);
  3250.     } catch(ex) {
  3251.       if (this.consoleDump) this.dump("Error enumerating sites: " + ex + "," + ex.stack);
  3252.     }
  3253.     return sites;
  3254.   },
  3255.   
  3256.   
  3257.   _collectPluginExtras: function(pluginExtras, extras) {
  3258.     for (var e, j = pluginExtras.length; j-- > 0;) {
  3259.       e = pluginExtras[j];
  3260.       if (e == extras) return false;
  3261.       if (e.mime == extras.mime && e.url == extras.url) {
  3262.         if (!e.placeholder) {
  3263.           pluginExtras.splice(j, 1, extras);
  3264.           return true;
  3265.         }
  3266.         return false;
  3267.       }
  3268.     }
  3269.     pluginExtras.push(extras);
  3270.     return true;
  3271.   },
  3272.   
  3273.   _silverlightPatch: function() {
  3274.     HTMLObjectElement.prototype.__defineGetter__("IsVersionSupported", function() {
  3275.       return (this.type == 'application/x-silverlight')
  3276.         ? function(n) { return true; } : undefined;
  3277.     });
  3278.   }.toSource(),
  3279.   
  3280.   _flashPatch: function() {
  3281.     var type = "application/x-shockwave-flash";
  3282.     var ver;
  3283.     var setAttribute = HTMLObjectElement.prototype.setAttribute;
  3284.     HTMLObjectElement.prototype.setAttribute = function(n, v) {
  3285.       if (n == "type" && v == type && !this.data) {
  3286.         this._pendingType = v;
  3287.         
  3288.         if (!ver) {
  3289.           this.SetVariable = function() {};
  3290.           this.GetVariable = function(n) {
  3291.             if (n != "$version") return;
  3292.             
  3293.             if (!ver) {
  3294.               ver = navigator.plugins["Shockwave Flash"]
  3295.                 .description.match(/(\d+)\.(\d+)(?:\s*r(\d+))?/);
  3296.               
  3297.               ver.shift();
  3298.               ver.push('99');
  3299.               ver = "WIN " + ver.join(",");
  3300.             }
  3301.             
  3302.             return;
  3303.           }
  3304.         }
  3305.         
  3306.         return;
  3307.       }
  3308.       setAttribute.call(this, n, v);
  3309.       if (n == "data" && ("_pendingType" in this) && this._pendingType == type) {
  3310.         this.setAttribute("type", type);
  3311.         this._pendingType = null;
  3312.       }
  3313.     };
  3314.  
  3315.   }.toSource(),
  3316.   
  3317.   applyPluginPatches: function(doc) {
  3318.     try {
  3319.       if (this.getExpando(doc, "pluginPatches")) return;
  3320.       this.setExpando(doc, "pluginPatches", true);
  3321.           
  3322.       var patches;
  3323.       
  3324.       if (this.forbidFlash && this.flashPatch) {
  3325.         (patches = patches || []).push(this._flashPatch);
  3326.         if (this.consoleDump & LOG_CONTENT_BLOCK) this.dump("Patching HTMLObject for SWFObject compatibility.");
  3327.       }
  3328.       if (this.forbidSilverlight && this.silverlightPatch) {
  3329.         (patches = patches || []).push(this._silverlightPatch);
  3330.         if (this.consoleDump & LOG_CONTENT_BLOCK) this.dump("Patching HTMLObject for Silverlight compatibility.");
  3331.       }
  3332.       
  3333.       if (!patches) return;
  3334.  
  3335.       ScriptSurrogate.execute(doc, "(" + patches.join(")();(") + ")();");
  3336.     } catch(e) {
  3337.        if (this.consoleDump) this.dump(e);
  3338.     }
  3339.   },
  3340.   
  3341.   _attachSilverlightExtras: function(embed, extras) {
  3342.     extras.silverlight = true;
  3343.     var pluginExtras = this.findPluginExtras(embed.ownerDocument);
  3344.     if (this._collectPluginExtras(pluginExtras, extras)) {
  3345.       extras.site = this.getSite(extras.url);
  3346.       try {
  3347.         // try to work around the IsInstalled() Silverlight machinery
  3348.         if (!embed.firstChild) { // dummy embed
  3349.           exras.dummy = true;
  3350.           return null;
  3351.         }
  3352.         extras.dummy = false;
  3353.       } catch(e) {
  3354.         if(this.consoleDump) this.dump(e);
  3355.       }
  3356.     }
  3357.     return embed;
  3358.   },
  3359.   
  3360.   _enumerateSites: function(browser, sites) {
  3361.  
  3362.     
  3363.     const nsIDocShell = CI.nsIDocShell;
  3364.     const nsIWebProgress = CI.nsIWebProgress;
  3365.     
  3366.     const docShells = browser.docShell.getDocShellEnumerator(
  3367.         CI.nsIDocShellTreeItem.typeContent,
  3368.         browser.docShell.ENUMERATE_FORWARDS
  3369.     );
  3370.     
  3371.     var loading = false, loaded = false, domLoaded = false;
  3372.     
  3373.     var docShell, docURI, url, win;
  3374.  
  3375.     var cache, redir, tmpPluginCount;
  3376.     
  3377.     var document, domain;
  3378.     while (docShells.hasMoreElements()) {
  3379.        
  3380.       docShell = docShells.getNext();
  3381.       document = (docShell instanceof nsIDocShell) &&
  3382.                  docShell.contentViewer && docShell.contentViewer.DOMDocument;
  3383.       if (!document) continue;
  3384.       
  3385.       // Truncate title as needed
  3386.       if (this.truncateTitle && document.title.length > this.truncateTitleLen) {
  3387.         document.title = document.title.substring(0, this.truncateTitleLen);
  3388.       }
  3389.       
  3390.       // Collect document / cached plugin URLs
  3391.       win = document.defaultView;
  3392.       url = this.getSite(docURI = document.documentURI);
  3393.       
  3394.       if (url) {
  3395.         try {
  3396.           if (document.domain && document.domain != this.getDomain(url, true) && url != "chrome:" && url != "about:blank") {
  3397.            // temporary allow changed document.domain on allow page
  3398.             if (this.getExpando(browser, "allowPageURL") == browser.docShell.currentURI.spec &&
  3399.                 this.getBaseDomain(document.domain).length >= document.domain.length &&
  3400.                 !(this.isJSEnabled(document.domain) || this.isUntrusted(document.domain))) {
  3401.              this.setTemp(site, true);
  3402.              this.setJSEnabled(document.domain, true);
  3403.              this.quickReload(win);
  3404.            }
  3405.            sites.unshift(document.domain);
  3406.           }
  3407.         } catch(e) {}
  3408.         
  3409.         sites.docSites.push(url);
  3410.         sites.push(url);
  3411.  
  3412.         for each(redir in this.getRedirCache(browser, docURI)) {
  3413.           sites.push(redir.site);
  3414.         }
  3415.       }
  3416.        
  3417.        
  3418.       tmpPluginCount = 0;
  3419.       
  3420.       domLoaded = this.getExpando(win, "contentLoaded");
  3421.       
  3422.       if (win == win.top) {
  3423.         cache = this.getExpando(document, "objectSites");
  3424.         if(cache) {
  3425.           if(this.consoleDump & LOG_CONTENT_INTERCEPT) this.dump("Adding plugin sites: " + cache.toSource());
  3426.           sites.push.apply(sites, cache);
  3427.           tmpPluginCount = cache.length;
  3428.           sites.pluginSites.push.apply(sites, cache);
  3429.         }
  3430.         
  3431.         cache = this.getExpando(document, "codeSites");
  3432.         if(cache) sites.push.apply(sites, cache);
  3433.         
  3434.         if (domLoaded) this.setExpando(browser, "allowPageURL", null);
  3435.       }
  3436.       
  3437.        // plugins
  3438.       this.processObjectElements(document, sites);
  3439.       
  3440.       loaded = !((docShell instanceof nsIWebProgress) && docShell.isLoadingDocument);
  3441.       if (!(domLoaded || loaded)) {
  3442.         sites.pluginCount += tmpPluginCount;
  3443.         loading = true;
  3444.         continue;
  3445.       }
  3446.  
  3447.        // scripts
  3448.       this.processScriptElements(document, sites);
  3449.     }
  3450.    
  3451.     var j;
  3452.     for (j = sites.length; j-- > 0;) {
  3453.       url = sites[j];
  3454.       if (/:/.test(url) && !(
  3455.           /^(?:file:\/\/|[a-z]+:\/*[^\/\s]+)/.test(url) || 
  3456.           this.getSite(url + "x") == url // doesn't this URL type support host?
  3457.         )) {
  3458.         sites.splice(j, 1); // reject scheme-only URLs
  3459.       }
  3460.     }
  3461.     
  3462.     
  3463.     sites.topURL = sites[0] || '';
  3464.     
  3465.     if (loading) try {
  3466.       browser.ownerDocument.defaultView.noscriptOverlay.syncUI();
  3467.     } catch(e) {}
  3468.     
  3469.     return this.sortedSiteSet(sites);
  3470.     
  3471.   },
  3472.   
  3473.   findOverlay: function(browser) {
  3474.     return browser && browser.ownerDocument.defaultView.noscriptOverlay;
  3475.   },
  3476.   
  3477.  
  3478.   // nsIChannelEventSink implementation
  3479.  
  3480.   onChannelRedirect: function(oldChannel, newChannel, flags) {
  3481.     const rw = this.requestWatchdog;
  3482.     const uri = newChannel.URI;
  3483.     
  3484.     if (HTTPS.forceURI(uri.clone())) {
  3485.       HTTPS.replaceChannel(newChannel);
  3486.     }
  3487.     
  3488.     IOUtil.attachToChannel(newChannel, "noscript.redirectFrom", oldChannel.URI);
  3489.     
  3490.     ABE.updateRedirectChain(oldChannel, newChannel);
  3491.     
  3492.     const ph = PolicyState.detach(oldChannel);
  3493.     try {
  3494.       if (ph) {
  3495.         // 0: aContentType, 1: aContentLocation, 2: aRequestOrigin, 3: aContext, 4: aMimeTypeGuess, 5: aInternalCall
  3496.         
  3497.         ph.contentLocation = uri;
  3498.         
  3499.         var ctx = ph.context;
  3500.         
  3501.         if (!this.isJSEnabled(oldChannel.URI.spec)) ph.requestOrigin = oldChannel.URI;
  3502.         try {
  3503.           ph.mimeType = newChannel.contentType || oldChannel.contentType || ph.mimeType;
  3504.         } catch(e) {}
  3505.         
  3506.         var browser, win;
  3507.         var type = ph.contentType;
  3508.         if(type != 6) { // not a document load? try to cache redirection for menus
  3509.           try {
  3510.             var site = this.getSite(uri.spec);
  3511.             win = IOUtil.findWindow(newChannel) || ctx && ((ctx instanceof CI.nsIDOMWindow) ? ctx : ctx.ownerDocument.defaultView); 
  3512.             browser = win && DOM.findBrowserForNode(win);
  3513.             if (browser) {
  3514.               this.getRedirCache(browser, win.top.document.documentURI)
  3515.                   .push({ site: site, type: type });
  3516.             } else {
  3517.               if (this.consoleDump) this.dump("Cannot find window for " + uri.spec);
  3518.             }
  3519.           } catch(e) {
  3520.             if (this.consoleDump) this.dump(e);
  3521.           }
  3522.           
  3523.           if (type == 7) {
  3524.             ph.extra = CP_FRAMECHECK;
  3525.             if (win && win.frameElement && ph.context != win.frameElement) {
  3526.               // this shouldn't happen
  3527.               if (this.consoleDump) this.dump("Redirected frame change for destination " + uri.spec);
  3528.               ph.context = win.frameElement;
  3529.             }
  3530.           }
  3531.           
  3532.         }
  3533.         
  3534.         if (this.shouldLoad.apply(this, ph.toArray()) != CP_OK) {
  3535.           if (this.consoleDump) {
  3536.             this.dump("Blocked " + oldChannel.URI.spec + " -> " + uri.spec + " redirection of type " + type);
  3537.           }
  3538.           throw "NoScript aborted redirection to " + uri.spec;
  3539.         }
  3540.         
  3541.         PolicyState.save(uri, ph);
  3542.       }
  3543.     } finally {
  3544.       PolicyState.reset();
  3545.     }
  3546.     
  3547.     // Document transitions
  3548.   
  3549.     if ((oldChannel.loadFlags & rw.DOCUMENT_LOAD_FLAGS) || (newChannel.loadFlags & rw.DOCUMENT_LOAD_FLAGS) && oldChannel.URI.prePath != uri.prePath) {
  3550.       if (newChannel instanceof CI.nsIHttpChannel)
  3551.         HTTPS.onCrossSiteRequest(newChannel, oldChannel.URI.spec,
  3552.                                browser || DOM.findBrowserForNode(IOUtil.findWindow(oldChannel)), rw);
  3553.       
  3554.       // docshell JS state management
  3555.       win = win || IOUtil.findWindow(oldChannel);
  3556.       this._handleDocJS2(win, oldChannel);
  3557.       this._handleDocJS1(win, newChannel);
  3558.     }
  3559.     
  3560.   },
  3561.   
  3562.   getRedirCache: function(browser, uri) {
  3563.     var redirCache = this.getExpando(browser, "redirCache", {});
  3564.     return redirCache[uri] || (redirCache[uri] = []);
  3565.   },
  3566.   
  3567.   recentlyBlocked: [],
  3568.   _recentlyBlockedMax: 40,
  3569.   recordBlocked: function(site) {
  3570.     var l = this.recentlyBlocked;
  3571.     var pos = l.lastIndexOf(site);
  3572.     
  3573.     if (pos > -1) {
  3574.       if (pos == l.length - 1) return;
  3575.       l.splice(pos, 1);
  3576.     }
  3577.     
  3578.     l.push(site);
  3579.     if (l.length > this._recentlyBlockedMax) {
  3580.       this.recentlyBlocked = l.slice(- this._recentlyBlockedMax / 2);
  3581.     }
  3582.   },
  3583.   
  3584.   // nsIWebProgressListener implementation
  3585.   onLinkIconAvailable: function(x) {}, // tabbrowser.xml bug?
  3586.   onStateChange: function(wp, req, stateFlags, status) {
  3587.     var ph;
  3588.     
  3589.     if (stateFlags & WP_STATE_START) {
  3590.       if (req instanceof CI.nsIChannel) { 
  3591.         // handle docshell JS switching and other early duties
  3592.         
  3593.         if (PolicyState.isChecking(req.URI)) {
  3594.           // ContentPolicy couldn't complete! DOS attack?
  3595.           PolicyState.removeCheck(req.URI);
  3596.           DOSChecker.abort(req);
  3597.           return;
  3598.         }
  3599.         
  3600.         if (req instanceof CI.nsIHttpChannel) {
  3601.           PolicyState.attach(req);
  3602.         }
  3603.         
  3604.         
  3605.         if ((stateFlags & WP_STATE_START_DOC) == WP_STATE_START_DOC) {
  3606.           if (req.URI.spec == "about:blank" && !IOUtil.extractInternalReferrer(req) ) {
  3607.            // new tab, we shouldn't touch its window otherwise we break stuff like newTabURL
  3608.            return;
  3609.           }
  3610.           
  3611.           // ns.dump(req.URI.spec + " state " + stateFlags + ", " + req.loadFlags +  ", pending " + req.isPending());
  3612.           
  3613.           var w = wp.DOMWindow;
  3614.           
  3615.           if (w) {
  3616.             
  3617.             if (w != w.top && w.frameElement) {
  3618.               ph = ph || PolicyState.extract(req);
  3619.               if (ph && this.shouldLoad(7, req.URI, ph.requestOrigin, w.frameElement, '', CP_FRAMECHECK) != CP_OK) { // late frame/iframe check
  3620.                 IOUtil.abort(req);
  3621.                 return;
  3622.               }
  3623.             }
  3624.  
  3625.             this._handleDocJS1(w, req);
  3626.             
  3627.           }
  3628.   
  3629.         } else try {
  3630.  
  3631.           ph = ph || PolicyState.extract(req); 
  3632.           
  3633.           if (!ph && req instanceof CI.nsIHttpChannel && wp.DOMWindow.document instanceof CI.nsIDOMXULDocument
  3634.                   && !/^(?:chrome|resource):/i.test(wp.DOMWindow.document.documentURI)) {
  3635.             if (!this.isJSEnabled(req.URI.prePath)) {
  3636.               IOUtil.abort(req);
  3637.               if (this.consoleDump & LOG_CONTENT_BLOCK) this.dump("Aborted XUL script " + req.URI.spec);
  3638.             }
  3639.           }
  3640.         } catch(e) {}
  3641.       }
  3642.     } else if ((stateFlags & WP_STATE_STOP))  {
  3643.       // STOP REQUEST
  3644.       if (req instanceof CI.nsIHttpChannel) {
  3645.         PolicyState.detach(req);
  3646.         ABERequest.clear(req); // release ABERequest, if any
  3647.       
  3648.         if (status === NS_ERROR_CONNECTION_REFUSED || status === NS_ERROR_NOT_AVAILABLE ||
  3649.             status === NS_ERROR_UNKNOWN_HOST) { // evict host from DNS cache to prevent DNS rebinding
  3650.           try {
  3651.             var host = req.URI.host;
  3652.             if (host) {
  3653.               if (status === NS_ERROR_UNKNOWN_HOST) {
  3654.                 DNS.invalidate(host);
  3655.               } else {
  3656.                 DNS.evict(host);
  3657.               }
  3658.             }
  3659.           } catch(e) {}
  3660.         }
  3661.       }
  3662.     }
  3663.   },
  3664.   onLocationChange: function(wp, req, location) {
  3665.     if (req && (req instanceof CI.nsIChannel)) try {        
  3666.       this._handleDocJS2(wp.DOMWindow, req);
  3667.       
  3668.       if (this.consoleDump & LOG_JS)
  3669.         this.dump("Location Change - req.URI: " + req.URI.spec + ", window.location: " +
  3670.                 (wp.DOMWindow && wp.DOMWindow.location.href) + ", location: " + location.spec);
  3671.  
  3672.       this.onBeforeLoad(req, wp.DOMWindow, location);
  3673.     } catch(e) {
  3674.       if (this.consoleDump) this.dump(e);
  3675.     }
  3676.   },
  3677.   onStatusChange: function(wp, req, status, msg) {
  3678.     if (status == 0x804b0003 && (req instanceof CI.nsIChannel) && !ABE.isDeferred(req)) { // DNS resolving, check if we need to clear the cache
  3679.       try {
  3680.         var host = req.URI.host;
  3681.         if (host) {
  3682.           var loadFlags = req.loadFlags;
  3683.           var cached = DNS.getCached(host);
  3684.           if (!cached || cached.expired ||
  3685.               loadFlags & LF_VALIDATE_ALWAYS ||
  3686.               loadFlags & LF_LOAD_BYPASS_ALL_CACHES) {
  3687.             if (cached) DNS.evict(host);
  3688.             
  3689.             ABE.log("Repeating ABE checks after DNS refresh for " + req.URI.spec);
  3690.             var abeReq = new ABERequest(req);
  3691.             this.requestWatchdog.handleABE(abeReq, loadFlags & req.LOAD_DOCUMENT_URI);
  3692.             
  3693.           }
  3694.         }
  3695.       } catch (e) {}
  3696.     }
  3697.   },
  3698.   onSecurityChange: function() {}, 
  3699.   onProgressChange: function() {},
  3700.   
  3701.   get _inclusionTypeInternalExceptions() {
  3702.     delete this._inclusionTypeInternalExceptions;
  3703.     return this._inclusionTypeInternalExceptions = new AddressMatcher("https://*.ebaystatic.com/*");
  3704.   },
  3705.   
  3706.   checkInclusionType: function(channel) {
  3707.     try {
  3708.       if (channel instanceof CI.nsIHttpChannel &&
  3709.           Math.round(channel.responseStatus / 100) != 3) {
  3710.         var ph = PolicyState.extract(channel);
  3711.         if (ph) {
  3712.           var ctype = ph.contentType;
  3713.           var origin = ABE.getOriginalOrigin(channel) || ph.requestOrigin;
  3714.           if (origin && (ctype === 2 || ctype === 3) && this.getBaseDomain(origin.host) != this.getBaseDomain(channel.URI.host)) {
  3715.  
  3716.             var mime;
  3717.             try {
  3718.               mime = channel.contentType;
  3719.             } catch (e) {
  3720.               mime = "UNKNOWN";
  3721.             }
  3722.             
  3723.             // a non-generic mime type has been given, let's check it strictly
  3724.             if (
  3725.                (ctype === 2
  3726.                   ? /\bj(?:avascript|s(?:on)?)\b/
  3727.                   : (PolicyUtil.isXSL(ph.context) ? /\bx[ms]l/ : /\bcss\b/)
  3728.                 ).test(mime)
  3729.               ) {
  3730.               return true;
  3731.             }
  3732.  
  3733.             var disposition;
  3734.             try {
  3735.               disposition = channel.getResponseHeader("Content-disposition");
  3736.             } catch(e) {}
  3737.  
  3738.             
  3739.             if (!disposition) {
  3740.               var url = channel.URI; 
  3741.               var ext = (url instanceof CI.nsIURL) ? url.fileExtension : '';
  3742.               
  3743.               if (ext &&
  3744.                   (ctype === 2 && /js(?:on)?/i.test(ext) ||
  3745.                    ctype == 3 && (ext == "css" || ext == "xsl" && (PolicyUtil.isXSL(ph.context))))
  3746.                 ) {
  3747.                 // extension matches and not an attachment, likely OK
  3748.                 return true; 
  3749.               }
  3750.               
  3751.               // extension doesn't match, let's check the mime
  3752.               
  3753.              
  3754.               if ((/^text\/.*ml$|unknown/i.test(mime)
  3755.                   || mime == "text/plain" && !(ext && /^(?:asc|log|te?xt)$/.test(ext)) // see Apache's magic file, turning any unkown ext file containing JS style comments into text/plain
  3756.                   )
  3757.                   && !this.getPref("inclusionTypeChecking.checkDynamic", false)) {
  3758.                 // text/html or xml, let's assume a misconfigured dynamically served script/css
  3759.                 if (this.consoleDump) this.dump(
  3760.                       "Warning: mime type " + mime + " for " +
  3761.                       (ctype == 2 ? "Javascript" : "CSS") + " served from " +
  3762.                      url.spec);
  3763.                 return true;
  3764.               }
  3765.             } else mime = mime + ", " + disposition;
  3766.             
  3767.             // every check failed, this is a fishy cross-site mistyped inclusion
  3768.             if (this._inclusionTypeInternalExceptions.testURI(url) ||
  3769.                 new AddressMatcher(this.getPref("inclusionTypeChecking.exceptions", "")).testURI(url))
  3770.               return true;
  3771.             this.log("[NoScript] Blocking cross site " + (ctype == 2 ? "Javascript" : "CSS") + " served from " +
  3772.                      channel.URI.spec +
  3773.                      " with wrong type info " + mime + " and included by " + origin.spec);
  3774.             IOUtil.abort(channel);
  3775.             return false;
  3776.           }
  3777.         }
  3778.       }
  3779.     } catch(e) {
  3780.       if (this.consoleDump) this.dump("Error checking inclusion type for " + channel.name + ": " + e);
  3781.     }
  3782.     return true;
  3783.   },
  3784.  
  3785.   onContentSniffed: function(req) {
  3786.     try {
  3787.       
  3788.       try {
  3789.         req.contentType;
  3790.         if (this.consoleDump & LOG_SNIFF)
  3791.           this.dump("OCS: " + req.URI.spec + ", " + req.contentType);
  3792.       } catch(e) {
  3793.         this.dump("OCS: " + req.URI.spec + ", CONTENT TYPE UNAVAILABLE YET");
  3794.         return;  // we'll check later in http-on-examine-merged-response
  3795.       }
  3796.       
  3797.       const domWindow = IOUtil.findWindow(req);
  3798.       if (domWindow && domWindow == domWindow.top) 
  3799.         return; // for top windows we call onBeforeLoad in onLocationChange
  3800.       
  3801.       var status = req.responseStatus;
  3802.       if (status >= 300 && status < 400) // redirect, wait for ultimate destination, see http://forums.informaction.com/viewtopic.php?f=7&t=2630
  3803.         return;
  3804.       
  3805.       
  3806.       if (ABE.checkFrameOpt(domWindow, req) &&
  3807.           this.getPref("frameOptions.enabled") &&
  3808.           !new AddressMatcher(this.getPref("frameOptions.parentWhitelist"))
  3809.             .test(domWindow.parent.location.href)
  3810.           ) {
  3811.         IOUtil.abort(req);
  3812.         this.showFrameOptError(domWindow, req.URI.spec);
  3813.         return; // canceled by frame options
  3814.       }
  3815.       this.onBeforeLoad(req, domWindow, req.URI);
  3816.     } catch(e) {
  3817.       if (this.consoleDump) this.dump(e);
  3818.     }
  3819.   },
  3820.   
  3821.   showFrameOptError: function(w, url) {
  3822.     this.log("X-FRAME-OPTIONS: blocked " + url, true);
  3823.     var f = w && w.frameElement;
  3824.     if (!f) return;
  3825.     const errPage = this.contentBase + "frameOptErr.xhtml";
  3826.     f.addEventListener("load", function(ev) {
  3827.       f.removeEventListener(ev.type, arguments.callee, false);
  3828.       if (errPage == f.contentWindow.location.href)
  3829.         f.contentWindow.document.getElementById("link")
  3830.           .setAttribute("href", url);
  3831.     }, false);
  3832.     f.contentWindow.location.replace(errPage);
  3833.   },
  3834.  
  3835.   
  3836.   onBeforeLoad: function(req, domWindow, location) {
  3837.     
  3838.     if (!domWindow) return;
  3839.     
  3840.     const uri = location;
  3841.     
  3842.     var docShell = null;
  3843.     
  3844.      
  3845.     var contentType;
  3846.     try {
  3847.       contentType = req.contentType;
  3848.     } catch(e) {
  3849.       contentType = "";
  3850.     }
  3851.     
  3852.     var contentDisposition = "";
  3853.    
  3854.     if (req instanceof CI.nsIHttpChannel) {
  3855.       
  3856.       try {
  3857.         contentDisposition = req.getResponseHeader("Content-disposition");
  3858.       } catch(e) {}
  3859.       
  3860.  
  3861.       if (domWindow.document)
  3862.         this.filterUTF7(req, domWindow, docShell = DOM.getDocShellForWindow(domWindow)); 
  3863.     }
  3864.     
  3865.     
  3866.     if (this.checkJarDocument(uri, domWindow)) {
  3867.       IOUtil.abort(req);
  3868.     }
  3869.   
  3870.     
  3871.     const topWin = domWindow == domWindow.top;
  3872.  
  3873.     var browser = null;
  3874.     var overlay = null;
  3875.     var xssInfo = null;
  3876.     
  3877.  
  3878.     if (topWin) {
  3879.       
  3880.       if (domWindow instanceof CI.nsIDOMChromeWindow) return;
  3881.     
  3882.       browser = DOM.findBrowserForNode(domWindow);
  3883.       overlay = this.findOverlay(browser);
  3884.       if (overlay) {
  3885.         overlay.setMetaRefreshInfo(null, browser);
  3886.         xssInfo = IOUtil.extractFromChannel(req, "noscript.XSS");
  3887.         if (xssInfo) xssInfo.browser = browser;
  3888.         this.requestWatchdog.unsafeReload(browser, false);
  3889.         
  3890.         if (!this.getExpando(browser, "clearClick")) {
  3891.           this.setExpando(browser, "clearClick", true);
  3892.           this.clearClickHandler.install(browser);
  3893.         }
  3894.       }
  3895.     }
  3896.     
  3897.     
  3898.     this._handleDocJS3(uri.spec, domWindow, docShell);
  3899.     
  3900.     
  3901.     
  3902.     if (!/^attachment\b/i.test(contentDisposition) &&
  3903.         this.shouldLoad(7, uri, uri, domWindow.frameElement || domWindow, contentType,
  3904.                         domWindow.frameElement ? CP_FRAMECHECK : CP_SHOULDPROCESS) != CP_OK) {
  3905.       
  3906.       req.loadFlags |= req.INHIBIT_CACHING;
  3907.       
  3908.       if (this.consoleDump & LOG_CONTENT_INTERCEPT)
  3909.         this.dump("Media document content type detected");
  3910.  
  3911.       if(!topWin) { 
  3912.         // check if this is an iframe
  3913.         
  3914.         if (domWindow.frameElement && !(domWindow.frameElement instanceof CI.nsIDOMHTMLFrameElement)
  3915.             && this.shouldLoad(5, uri, IOS.newURI(domWindow.parent.location.href, null, null),
  3916.                 domWindow.frameElement, contentType, CP_SHOULDPROCESS) == CP_OK)
  3917.             return;
  3918.         
  3919.         if (this.consoleDump & LOG_CONTENT_BLOCK) 
  3920.           this.dump("Deferring framed media document");
  3921.         
  3922.         var url = uri.spec;
  3923.         
  3924.         browser = browser || DOM.findBrowserForNode(domWindow);
  3925.         this.getRedirCache(browser, domWindow.top.document.documentURI).push({site: this.getSite(url), type: 7});
  3926.         // defer separate embed processing for frames
  3927.         
  3928.         
  3929.        
  3930.         docShell = docShell || DOM.getDocShellForWindow(domWindow);
  3931.         docShell.loadURI("data:" + req.contentType + ",",
  3932.                              CI.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
  3933.                              null, null, null);
  3934.         Thread.asap(function() {
  3935.           IOUtil.abort(req);
  3936.           if (docShell) {
  3937.             var doc = docShell.document;
  3938.             docShell.stop(0);
  3939.             docShell.loadURI(ns.createPluginDocumentURL(url,
  3940.               doc.body && doc.body.firstChild && doc.body.firstChild.tagName),
  3941.                              CI.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
  3942.                              null, null, null);
  3943.           }
  3944.         });
  3945.         
  3946.         return;
  3947.       }
  3948.       
  3949.       if (this.consoleDump & LOG_CONTENT_BLOCK) 
  3950.         this.dump("Blocking top-level plugin document");
  3951.  
  3952.       IOUtil.abort(req);
  3953.       
  3954.       
  3955.       ["embed", "video", "audio"].forEach(function(tag) {
  3956.         var embeds = domWindow.document.getElementsByTagName(tag);
  3957.         var eType = "application/x-noscript-blocked";
  3958.         var eURL = "data:" + eType + ",";
  3959.         var e;
  3960.         for (var j = embeds.length; j-- > 0;) {
  3961.           e = embeds.item(j);
  3962.           if (this.shouldLoad(5, uri, null, e, contentType, CP_SHOULDPROCESS) != CP_OK) {
  3963.             e.src = eURL;
  3964.             e.type = eType;
  3965.           }
  3966.         }
  3967.       }, this);
  3968.       
  3969.       if (xssInfo) overlay.notifyXSS(xssInfo);
  3970.       
  3971.       return;
  3972.  
  3973.     } else {
  3974.       if (topWin) {
  3975.         if (xssInfo) overlay.notifyXSSOnLoad(xssInfo);
  3976.       }
  3977.     }
  3978.   },
  3979.   
  3980.   get clearClickHandler() {
  3981.       const cch = new ClearClickHandler(this);
  3982.       this.__defineGetter__("clearClickHandler", function() { return cch; })
  3983.       return cch;
  3984.   },
  3985.   
  3986.   _handleDocJS1: function(win, req) {
  3987.     
  3988.     const abeSandboxed = ABE.isSandboxed(req);
  3989.     const docShellJSBlocking = this.docShellJSBlocking || abeSandboxed;
  3990.     
  3991.     
  3992.     if (!docShellJSBlocking || (win instanceof CI.nsIDOMChromeWindow)) return;
  3993.     
  3994.     
  3995.     try {
  3996.       
  3997.       var docShell = DOM.getDocShellForWindow(win) ||
  3998.                      DOM.getDocShellForWindow(IOUtil.findWindow(req));
  3999.       
  4000.       var url = req.URI.spec;
  4001.       if (!/^https?:/.test(url)) url = req.originalURI.spec;
  4002.  
  4003.       if (!docShell) {
  4004.         if (this.consoleDump) this.dump("DocShell not found for JS switching in " + url);
  4005.         return;
  4006.       }
  4007.       
  4008.       if (abeSandboxed) {
  4009.         ABE.sandbox(docShell);
  4010.         return;
  4011.       }
  4012.       
  4013.       var jsEnabled;
  4014.       if (docShellJSBlocking & 2) { // block not whitelisted
  4015.         jsEnabled = url && this.isJSEnabled(this.getSite(url)) || /^about:/.test(url);
  4016.       } else if (docShellJSBlocking & 1) { // block untrusted only
  4017.         var site = this.getSite(url);
  4018.         jsEnabled = !(this.isUntrusted(site) || this.isForbiddenByHttpsStatus(site));
  4019.       } else return;
  4020.       
  4021.       const dump = this.consoleDump & LOG_JS;
  4022.       const prevStatus = docShell.allowJavascript;
  4023.       
  4024.       // Trying to be kind with other docShell-level blocking apps (such as Tab Mix Plus), we
  4025.       // check if we're the ones who actually blocked this docShell, or if this channel is out of our control
  4026.       var prevBlocked = this.getExpando(win.document, "prevBlocked");
  4027.       prevBlocked = prevBlocked ? prevBlocked.value : "?";
  4028.  
  4029.       if (dump)
  4030.         this.dump("DocShell JS Switch: " + url + " - " + jsEnabled + "/" + prevStatus + "/" + prevBlocked);
  4031.       
  4032.       if (jsEnabled && !prevStatus) {
  4033.         
  4034.         // be nice with other blockers
  4035.         if (!prevBlocked) return;
  4036.         
  4037.         // purge body events
  4038.         try {
  4039.           var aa = win.document.body && win.document.body.attributes;
  4040.           if (aa) for (var j = aa.length; j-- > 0;) {
  4041.             if(/^on/i.test(aa[j].name)) aa[j].value = "";
  4042.           }
  4043.         } catch(e1) {
  4044.           if (this.consoleDump & LOG_JS)
  4045.             this.dump("Error purging body attributes: " + e2);
  4046.         }
  4047.       }
  4048.       
  4049.       IOUtil.attachToChannel(req, "noscript.dsjsBlocked",
  4050.                 { value: // !jsEnabled && (prevBlocked || prevStatus)
  4051.                         // we're the cause of the current disablement if
  4052.                         // we're disabling and (was already blocked by us or was not blocked)
  4053.                         !(jsEnabled || !(prevBlocked || prevStatus)) // De Morgan for the above, i.e.
  4054.                         // we're the cause of the current disablement unless
  4055.                         // we're enabling or (was already blocked by someone else = was not (blocked by us or enabled))
  4056.                         // we prefer the latter because it coerces to boolean
  4057.                 });
  4058.       
  4059.       docShell.allowJavascript = jsEnabled;
  4060.     } catch(e2) {
  4061.       if (this.consoleDump & LOG_JS)
  4062.         this.dump("Error switching DS JS: " + e2);
  4063.     }
  4064.   },
  4065.   
  4066.   _handleDocJS2: function(win, req) {
  4067.     // called at the beginning of onLocationChange
  4068.     if (win)
  4069.       this.setExpando(win.document, "prevBlocked",
  4070.         IOUtil.extractFromChannel(req, "noscript.dsjsBlocked")
  4071.       );
  4072.   },
  4073.   
  4074.   _handleDocJS3: function(url, win, docShell) {
  4075.     // called at the end of onLocationChange
  4076.     var jsBlocked = docShell && !docShell.allowJavascript || !(this.jsEnabled || this.isJSEnabled(this.getSite(url)));
  4077.     
  4078.     
  4079.     
  4080.     if (jsBlocked) {
  4081.       if (this.getPref("fixLinks")) {
  4082.         win.addEventListener("click", this.bind(this.onContentClick), true);
  4083.         win.addEventListener("change", this.bind(this.onContentChange), true);
  4084.       }
  4085.       return;
  4086.     }
  4087.     
  4088.     ScriptSurrogate.apply(win.document, url, url);
  4089.     try {
  4090.       if(this.jsHackRegExp && this.jsHack && this.jsHackRegExp.test(url) && !win._noscriptJsHack) {
  4091.         try {
  4092.           win._noscriptJsHack = true;
  4093.           ScriptSurrogate.execute(win, this.jsHack);
  4094.         } catch(jsHackEx) {}
  4095.       }
  4096.     } catch(e) {}
  4097.     
  4098.   },
  4099.   
  4100.   beforeManualAllow: function(win) {
  4101.     // reset prevBlock info, to forcibly allow docShell JS
  4102.     this.setExpando(win.document, "prevBlocked", { value: "m" });
  4103.   },
  4104.   
  4105.   handleErrorPage: function(win, uri) {
  4106.     win = win && win.contentWindow || win;
  4107.     if (!win) return;
  4108.     var docShell = DOM.getDocShellForWindow(win);
  4109.     if (!docShell) return;
  4110.     
  4111.     docShell.allowJavascript = true;
  4112.     
  4113.     if (!this.getPref("STS.expertErrorUI"))
  4114.       STS.patchErrorPage(docShell, uri);
  4115.   },
  4116.   
  4117.   checkJarDocument: function(uri, context, origin) {
  4118.     if (this.forbidJarDocuments && (uri instanceof CI.nsIJARURI) &&
  4119.       !(/^(?:file|resource|chrome)$/.test(uri.JARFile.scheme) ||
  4120.           this.forbidJarDocumentsExceptions &&
  4121.           this.forbidJarDocumentsExceptions.test(uri.spec)) &&
  4122.       (origin && origin.prePath != uri.prePath)
  4123.       ) {
  4124.       if (context && this.getPref("jarDoc.notify", true)) {
  4125.         var window = (context instanceof CI.nsIDOMWindow) && context || 
  4126.           (context instanceof CI.nsIDOMDocumentView) && context.defaultView || 
  4127.           (context instanceof CI.nsIDOMNode) && context.ownerDocument && context.ownerDocument.defaultView;
  4128.         if (window) {
  4129.           this.delayExec(this.displayJarFeedback, 10, {
  4130.             context: context,
  4131.             uri: uri.spec
  4132.           });
  4133.         } else {
  4134.           this.dump("checkJarDocument -- window not found");
  4135.         }
  4136.       }
  4137.       this.log("[NoScript] " + this.getString("jarDoc.notify", [uri.spec]));
  4138.       return true;
  4139.     }
  4140.     return false;
  4141.   },
  4142.   
  4143.  
  4144.   
  4145.   displayJarFeedback: function(info) {
  4146.     var doc = (info.context instanceof CI.nsIDOMDocument) && info.context || 
  4147.       info.context.contentDocument || info.context.document || info.context.ownerDocument;
  4148.     if (!doc) {
  4149.       ns.dump("displayJarFeedback -- document not found");
  4150.       return;
  4151.     }
  4152.     var browser = DOM.findBrowserForNode(doc);
  4153.     if (browser) {
  4154.       var overlay = ns.findOverlay(browser);
  4155.       if (overlay && overlay.notifyJarDocument({
  4156.           uri: info.uri,
  4157.           document: doc
  4158.       })) return;
  4159.     } else {
  4160.       ns.dump("displayJarFeedback -- browser not found... falling back to content notify");
  4161.     }
  4162.     
  4163.     var message = ns.getString("jarDoc.notify", [SiteUtils.crop(info.uri)]) + 
  4164.       "\n\n" + ns.getString("jarDoc.notify.reference");
  4165.     
  4166.     var rootNode = doc.documentElement.body || doc.documentElement;
  4167.     const containerID = "noscript-jar-feedback";
  4168.     var container = doc.getElementById(containerID);
  4169.     if (container) container.parentNode.removeChild(container);
  4170.     container = rootNode.insertBefore(doc.createElementNS(HTML_NS, "div"), rootNode.firstChild || null);
  4171.     with (container.style) {
  4172.       backgroundColor = "#fffff0";
  4173.       borderBottom = "1px solid #444";
  4174.       color = "black";
  4175.       backgroundImage = "url(" + ns.pluginPlaceholder + ")";
  4176.       backgroundPosition = "left top";
  4177.       backgroundRepeat = "no-repeat";
  4178.       paddingLeft = "40px";
  4179.       margin = "0px";
  4180.       parring = "8px";
  4181.     }
  4182.     container.id = "noscript-jar-feedback";
  4183.     var description = container.appendChild(doc.createElementNS(HTML_NS, "pre"));
  4184.     description.appendChild(doc.createTextNode(message));
  4185.     description.innerHTML = description.innerHTML
  4186.     .replace(/\b(http:\/\/noscript\.net\/faq#jar)\b/g, 
  4187.               '<a href="$1" title="NoScript JAR FAQ">$1</a>'); 
  4188.   },
  4189.   // end nsIWebProgressListener
  4190.   
  4191.   filterUTF7: function(req, window, ds) {
  4192.     try {
  4193.       if (!ds) return;
  4194.       var as = CC["@mozilla.org/atom-service;1"].getService(CI.nsIAtomService);
  4195.       if(window.document.characterSet == "UTF-7" ||
  4196.         !req.contentCharset && (ds.documentCharsetInfo.parentCharset + "") == "UTF-7") {
  4197.         if(this.consoleDump) this.dump("Neutralizing UTF-7 charset!");
  4198.         ds.documentCharsetInfo.forcedCharset = as.getAtom("UTF-8");
  4199.         ds.documentCharsetInfo.parentCharset = ds.documentCharsetInfo.forcedCharset;
  4200.         ds.reload(ds.LOAD_FLAGS_CHARSET_CHANGE); // neded in Gecko > 1.9
  4201.       }
  4202.     } catch(e) { 
  4203.       if(this.consoleDump) this.dump("Error filtering charset on " + req.name + ": " + e) 
  4204.     }
  4205.   },
  4206.   
  4207.   _attemptNavigationInternal: function(doc, destURL, callback) {
  4208.     var cs = doc.characterSet;
  4209.     var uri = IOS.newURI(destURL, cs, IOS.newURI(doc.documentURI, cs, null));
  4210.     
  4211.     if (/^https?:\/\//i.test(destURL)) callback(doc, uri);
  4212.     else {
  4213.       var req = ns.createCheckedXHR("HEAD", uri.spec);
  4214.       req.open("HEAD", uri.spec);
  4215.       var done = false;
  4216.       req.onreadystatechange = function() {
  4217.         
  4218.         if (req.readystate < 2) return;
  4219.         try {
  4220.           if (!done && req.status) {
  4221.             done = true;
  4222.             if (req.status == 200) callback(doc, uri);
  4223.             req.abort();
  4224.           }
  4225.         } catch(e) {}
  4226.       }
  4227.       req.send(null);
  4228.     }
  4229.   },
  4230.   attemptNavigation: function(doc, destURL, callback) {
  4231.     // delay is needed on Gecko < 1.9 to detach browser context
  4232.     this.delayExec(this._attemptNavigationInternal, 0, doc, destURL, callback);
  4233.   },
  4234.   
  4235.   // simulate onchange on selects if options look like URLs
  4236.   onContentChange: function(ev) {
  4237.     var s = ev.originalTarget;
  4238.     if (!(s instanceof CI.nsIDOMHTMLSelectElement) ||
  4239.         s.hasAttribute("multiple") ||
  4240.         !/open|nav|location|\bgo|load/i.test(s.getAttribute("onchange"))) return;
  4241.     
  4242.     var doc = s.ownerDocument;
  4243.     var url = doc.documentURI;
  4244.     if (this.isJSEnabled(this.getSite(url))) return;
  4245.     
  4246.     var opt = s.options[s.selectedIndex];
  4247.     if (!opt) return;
  4248.     
  4249.     if (/[\/\.]/.test(opt.value) && opt.value.indexOf("@") < 0) {
  4250.       this.attemptNavigation(doc, opt.value, function(doc, uri) {
  4251.         doc.defaultView.location.href = uri.spec;
  4252.       });
  4253.       ev.preventDefault();
  4254.     }
  4255.   },
  4256.   
  4257.   onContentClick: function(ev) {
  4258.     
  4259.     if (ev.button == 2) return;
  4260.     
  4261.     var a = ev.originalTarget;
  4262.     
  4263.     if (a.__noscriptFixed) return;
  4264.     
  4265.     var doc = a.ownerDocument;
  4266.     var url = doc.documentURI;
  4267.     if (this.isJSEnabled(this.getSite(url))) return;
  4268.     
  4269.     var onclick;
  4270.     
  4271.     while (!(a instanceof CI.nsIDOMHTMLAnchorElement || a instanceof CI.nsIDOMHTMLAreaElement)) {
  4272.       if (typeof(a.getAttribute) == "function" && (onclick = a.getAttribute("onclick"))) break;
  4273.       if (!(a = a.parentNode)) return;
  4274.     }
  4275.     
  4276.     const href = a.getAttribute("href");
  4277.     // fix JavaScript links
  4278.     var jsURL;
  4279.     if (href) {
  4280.       jsURL = /^javascript:/.test(href);
  4281.       if (!(jsURL || href == "#")) return;
  4282.     } else {
  4283.       jsURL = "";
  4284.     }
  4285.     
  4286.     onclick = onclick || a.getAttribute("onclick");
  4287.     var fixedHref = (onclick && this.extractJSLink(onclick)) || 
  4288.                      (jsURL && this.extractJSLink(href)) || "";
  4289.     
  4290.     onclick = onclick || href;
  4291.     
  4292.     if (/\bsubmit\s*\(\s*\)/.test(onclick)) {
  4293.       var form;
  4294.       if (fixedHref) {
  4295.         form = doc.getElementById(fixedHref); // youtube
  4296.         if (!(form instanceof CI.nsIDOMHTMLFormElement)) {
  4297.           form = doc.forms.namedItem(fixedHref);   
  4298.         }
  4299.       }
  4300.       if (!form) {
  4301.         var m = onclick.match(/(?:(?:\$|document\.getElementById)\s*\(\s*["']#?([\w\-]+)[^;]+|\bdocument\s*\.\s*(?:forms)?\s*(?:\[\s*["']|\.)?([^\.\;\s"'\]]+).*)\.submit\s*\(\)/);
  4302.         form = m && (/\D/.test(m[1]) ? (doc.forms.namedItem(m[1]) || doc.getElementById(m[1])) : doc.forms.item(parseInt(m[1])));
  4303.         if (!(form && (form instanceof CI.nsIDOMHTMLFormElement))) {
  4304.           while (form = a.parentNode && form != doc && !form instanceof CI.nsIDOMHTMLFormElement);
  4305.         }
  4306.       }
  4307.       if (form && (form instanceof CI.nsIDOMHTMLFormElement)) {
  4308.         form.submit();
  4309.         ev.preventDefault();
  4310.       }
  4311.       return;
  4312.     }
  4313.     
  4314.     if (fixedHref) {
  4315.       var callback;
  4316.       if (/^(?:button|input)$/i.test(a.tagName)) { // JS button
  4317.         if (a.type == "button" || (a.type == "submit" && !a.form)) {
  4318.           callback = function(doc, uri) { doc.defaultView.location.href = uri.spec; }; 
  4319.         } else return;
  4320.       } else {
  4321.         var evClone = doc.createEvent("MouseEvents");
  4322.         evClone.initMouseEvent("click",ev.canBubble, ev.cancelable, 
  4323.                            ev.view, ev.detail, ev.screenX, ev.screenY, 
  4324.                            ev.clientX, ev.clientY, 
  4325.                            ev.ctrlKey, ev.altKey, ev.shiftKey, ev.metaKey,
  4326.                            ev.button, ev.relatedTarget);
  4327.         callback =
  4328.           function(doc, uri) {
  4329.             a.setAttribute("href", fixedHref);
  4330.             var title = a.getAttribute("title");
  4331.             a.setAttribute("title", title ? "[js] " + title : 
  4332.               (onclick || "") + " " + href
  4333.             );
  4334.             a.dispatchEvent(ev = evClone); // do not remove "ev = " -- for some reason, it works this way only :/
  4335.           };
  4336.         a.__noscriptFixed = true;
  4337.       }
  4338.       if (callback) {
  4339.         if (typeof(/ /) == "function") ev.preventDefault(); // Gecko < 1.9
  4340.         this.attemptNavigation(doc, fixedHref, callback);
  4341.         ev.preventDefault();
  4342.       }
  4343.     } else { // try processing history.go(n) //
  4344.       if(!onclick) return;
  4345.       
  4346.       jsURL = onclick.match(/history\s*\.\s*(?:go\s*\(\s*(-?\d+)\s*\)|(back|forward)\s*\(\s*)/);
  4347.       jsURL = jsURL && (jsURL = jsURL[1] || jsURL[2]) && (jsURL == "back" ? -1 : jsURL == "forward" ? 1 : jsURL); 
  4348.  
  4349.       if (!jsURL) return;
  4350.       // jsURL now has our relative history index, let's navigate
  4351.  
  4352.       var ds = DOM.getDocShellForWindow(doc.defaultView);
  4353.       if (!ds) return;
  4354.       var sh = ds.sessionHistory;
  4355.       if (!sh) return;
  4356.       
  4357.       var idx = sh.index + jsURL;
  4358.       if (idx < 0 || idx >= sh.count) return; // out of history bounds 
  4359.       ds.gotoIndex(idx);
  4360.       ev.preventDefault(); // probably not needed
  4361.     }
  4362.   },
  4363.   
  4364.   extractJSLink: function(js) {
  4365.     const findLink = /(['"])([\/\w-\?\.#%=&:@]+)\1/g;
  4366.     findLink.lastIndex = 0;
  4367.     var maxScore = -1;
  4368.     var score; 
  4369.     var m, s, href;
  4370.     while ((m = findLink.exec(js))) {
  4371.       s = m[2];
  4372.       if (/^https?:\/\//.test(s)) return s;
  4373.       score = 0;
  4374.       if (s.indexOf("/") > -1) score += 2;
  4375.       if (s.indexOf(".") > 0) score += 1;
  4376.       if (score > maxScore) {
  4377.         maxScore = score;
  4378.         href = s;
  4379.       }
  4380.     }
  4381.     return href || "";
  4382.   },
  4383.   
  4384.   createXSanitizer: function() {
  4385.     return new XSanitizer(this.filterXGetRx, this.filterXGetUserRx);
  4386.   },
  4387.   
  4388.   get compatEvernote() {
  4389.     delete this.compatEvernote;
  4390.     return this.compatEvernote = ("IWebClipper3" in CI) && this.getPref("compat.evernote") && {
  4391.       onload: function(ev) {
  4392.         var f = ev.currentTarget;
  4393.         if ((f.__evernoteLoadCount = (f.__evernoteLoadCount || 0) + 1) >= 7) {
  4394.           f.removeEventListener(ev.type, arguments.callee, false);
  4395.           var id = f.id.replace(/iframe/g, "clipper");
  4396.           for (var box = f.parentNode; box && box.id != id; box = box.parentNode);
  4397.           if (box) box.parentNode.removeChild(box);
  4398.         }
  4399.       }
  4400.     }
  4401.   },
  4402.   
  4403.   get compatGNotes() {
  4404.     delete this.compatGNotes;
  4405.     return this.compatGNotes = ("@google.com/gnotes/app-context;1" in CC) && this.getPref("compat.gnotes") &&
  4406.       "http://www.google.com/notebook/static_files/blank.html";
  4407.   },
  4408.   
  4409.   consoleService: CC["@mozilla.org/consoleservice;1"].getService(CI.nsIConsoleService),
  4410.   
  4411.   log: function(msg, dump) {
  4412.     this.consoleService.logStringMessage(msg);
  4413.     if (dump) this.dump(msg, true);
  4414.   },
  4415.  
  4416.   dump: function(msg, noConsole) {
  4417.     msg = "[NoScript] " + msg;
  4418.     dump(msg + "\n");
  4419.     if(this.consoleLog && !noConsole) this.log(msg);
  4420.   },
  4421.   
  4422.   versionChecked: false,
  4423.   checkVersion: function() {
  4424.     if (this.versionChecked) return;
  4425.     this.versionChecked = true;
  4426.     
  4427.     const ver =  this.VERSION;
  4428.     const prevVer = this.getPref("version", "");
  4429.     if (prevVer != ver) {
  4430.       this.setPref("version", ver);
  4431.       this.savePrefs();
  4432.       if (this.getPref("firstRunRedirection", true)) {
  4433.         this.delayExec(function() {
  4434.          
  4435.           const name = EXTENSION_NAME;
  4436.           const domain = name.toLowerCase() + ".net";
  4437.           var url = "http://" + domain + "/?ver=" + ver;
  4438.           var hh = "X-IA-Post-Install: " + name + " " + ver;
  4439.           if (prevVer) {
  4440.             url += "&prev=" + prevVer;
  4441.             hh += "; updatedFrom=" + prevVer;
  4442.           }
  4443.           hh += "\r\n";
  4444.           
  4445.           var hs = CC["@mozilla.org/io/string-input-stream;1"] .createInstance(CI.nsIStringInputStream);
  4446.           hs.setData(hh, hh.length); 
  4447.           
  4448.           var browser = DOM.mostRecentBrowserWindow.getBrowser();
  4449.           var b = (browser.selectedTab = browser.addTab()).linkedBrowser;
  4450.           b.stop();
  4451.           b.webNavigation.loadURI(url, CI.nsIWebNavigation.FLAGS_NONE, null, null, hs);
  4452.           
  4453.         }, 500);
  4454.       }
  4455.     }
  4456.   }
  4457.   
  4458. }
  4459.  
  4460. ns.wrappedJSObject = ns;
  4461. ns.register();
  4462.  
  4463. if ("nsIChromeRegistrySea" in CI) INCLUDE("SMUninstaller");